Artículos de la serie de componentes Android Jetpack: Componente
Android Jetpack (1) Componente LifeCycle
Android Jetpack (2) Navegación
Componente Android Jetpack (3) ViewModel Componente
Android Jetpack (4) LiveData Componente
Android Jetpack (5)
Componente Room Android JetPack (6) Enlace de datos
Android Jetpack Componentes (7) Paginación de
componentes de Android Jetpack (8) WorkManager
Primer idioma
Para las aplicaciones que admiten el cambio de pantalla horizontal y vertical, cuando cambiamos entre pantallas horizontales y verticales, Activity
se volverán a crear y debemos considerar el almacenamiento y la recuperación de datos. Jetpack nos proporciona componentes de ViewModel para ayudarnos a resolver este problema. ViewModel almacena y administra los datos relacionados con la interfaz de una manera orientada al ciclo de vida. ViewModel es independiente de los cambios de configuración, incluso si se Activity
reconstruye, no afectará el ciclo de vida de ViewModel.
En el desarrollo de aplicaciones, la lógica empresarial, como la interacción de la interfaz de usuario y la adquisición de datos, generalmente se escribe en la página. Cuando los requisitos del proyecto continúan aumentando y las funciones de la página son complejas, la clase de página aparecerá particularmente hinchada y no adecuada para el mantenimiento. Esto también viola el "principio de función única". La página solo debe ser responsable de manejar la interacción entre el usuario y los controles de la interfaz de usuario y la visualización de datos, y la lógica comercial para obtener datos debe manejarse por separado.
Android proporciona la clase ViewModel específicamente para almacenar los datos requeridos por la página de la aplicación. Puede entenderse como un puente entre la vista y el modelo de datos, que separa la vista de los datos mientras mantiene la comunicación.
ViewModel 与 onSaveInstanceState ()
Por lo general, lo usamos onSaveInstanceState()
para resolver el problema de la pérdida de datos causada por la rotación de la pantalla, pero solo puede guardar una pequeña cantidad de datos que admiten la serialización, y Viewmodel admite todos los datos de la página. Cabe señalar que ViewModel no admite la persistencia de datos. Cuando la interfaz se destruye por completo, ViewModel y los datos que contiene dejarán de existir. onSaveInstanceState()
Sin esta limitación, los datos de la página pueden persistir y los dos propósitos son diferente.
confiar
//包含了 viewmodel 和 livedata,lifecycle-extensions 中的 API 已弃用
//implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
//或者指明使用viewmodel
implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
usar
Primero, personalice un ViewModel y herede la clase ViewModel.
public class HomeViewModel extends ViewModel {
@Override
protected void onCleared() {
super.onCleared();
}
}
ViewModel es una clase abstracta y solo hay una onClear()
. Activity
Este método se llamará cuando se destruya el ViewModel asociado a él. Se pueden realizar algunos trabajos relacionados con la liberación de recursos en el método. Tenga en cuenta que la Activity
reconstrucción causada por la rotación de la pantalla no llamará este método.
Usamos un ejemplo de un contador para mostrar el uso de ViewModel para separar la vista de los datos.
private int value;
public String gtValue() {
return String.valueOf(value);
}
public void addValue() {
value+=1;
if (onChangedListener != null) {
onChangedListener.onChanged(String.valueOf(value));
}
}
/**
* 通过接口的形式将数据传递出去,更好的方式是通过LiveData,
*/
public interface onChangedListener {
void onChanged(String value);
}
private onChangedListener onChangedListener;
public void setOnChangeListener(onChangedListener onChangeListener) {
this.onChangedListener = onChangeListener;
}
Luego Activity
, cada vez que haces clic, el contador es +1, y se completa la instanciación del ViewModel ViewModelProvider
. Este determinará si el ViewModel existe, si existe, regresará directamente, y si no existe, se creará. .
HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
textView.setText(homeViewModel.gtValue());
button.setOnClickListener(view -> homeViewModel.addValue());
homeViewModel.setOnChangeListener(textView::setText);
Al ejecutar el código, puede encontrar que el contador de clics será +1 y los datos no desaparecerán cuando la actividad se reconstruya girando la pantalla, lo que significa que ViewModel no se ha destruido y los datos retenidos siempre han existido.
Compartir datos entre fragmentos
Activity
Dos o más a Fragment
menudo necesitan comunicarse entre sí, este proceso es más complejo, los datos pueden ViewModel del Activity
tratamiento de liberación, siempre y cuando Activity
no se destruyan, ViewModel ha existido, en base a estas características, una pluralidad de los Fragment
mismos se pueden usar Activity
rango compartido por ViewModel Maneja este tipo de comunicación.
//ViewModelProvider的范围必须是所在activity
HomeViewModel homeViewModel = new ViewModelProvider(getActivity()).get(HomeViewModel.class);
Fragment
ViewModel también se instancia en el otro . Al cambiar Fragment
, solicitará el valor actual del contador para alcanzar la Fragment
comunicación entre.
homeViewModel = new ViewModelProvider(getActivity()).get(HomeViewModel.class);
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
Toast.makeText(getActivity(), homeViewModel.gtValue(),Toast.LENGTH_SHORT).show();
}
En Fragment
uso ViewModel y Activity
similar usado.
Principio de ViewModel
ViewModel recibe un ViewModelStoreOwner
objeto como parámetro, le pasamos este, esto es por Activity
herencia ComponentActivity
, implementa la ViewModelStoreOwner
interfaz por defecto . El código fuente es el siguiente.
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
La responsabilidad de implementar esta interfaz es mantener la propiedad durante los cambios de configuración ViewModelStore
y ser llamado cuando el alcance está a punto de ser destruido.
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
public ComponentActivity() {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
getViewModelStore()
El tipo devuelto es ViewModelStore
. Como se puede ver en el código fuente, el ViewModel se HashMap<String,ViewModel>
almacena en caché en la forma, como se mencionó anteriormente, cuando la página necesita el ViewModel, primero se determina si existe en el caché.
Fragment
La ViewModelStoreOwner
interfaz también está implementada de forma predeterminada y el principio es Activity
similar.
Cabe señalar que al crear una instancia del ViewModel, no pase ningún tipo Context
u Context
objeto referenciado, lo que provocará una pérdida de memoria.
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
AndroidViewModel
Si la instanciación de ViewModel para pasar Context
un objeto, puede usar AndroidViewModel
la clase que hereda de ViewModel, y recibe Application
como Context
, su ciclo de vida y así usar Application
el mismo ciclo de vida, no conduce a una pérdida de memoria, mientras que el tratamiento de datos en una escena específica .