学习android的同学应该都了解Activity,关于Activity的相关介绍也有很多,下面简要罗列出Activity的生命周期:
上图列出了Activity在不同情况下的生命周期变化,内容来自Android开发艺术探索。
补充:
1. Activity之间的跳转,比如A->B,生命周期的变化情况:
A: onPause -> B: onCreate -> B: onStart -> B: onResume -> A: onStop
由于A的onPause调用之后会创建B Activity,所以不适合在onPause中进行一些耗时操作,可以在onStop中进行操作。
2. Activity在资源配置发生改变以及被系统回收情况下的生命周期变化:
当Activity在异常情况下被销毁时,其生命周期变化会略有不同,最常见的就是屏幕翻转导致Activity销毁重建,此时有些数据便会丢失,但某些数据不会,这是因为Activity在异常情况下被销毁时,会调用onSaveInstanceState方法来保存一些状态,也会调用View的onSaveInstanceState来保存View的一些状态,而在恢复时会将保存的数据传递给onCreate和onRestoreInstanceState中的savedInstanceState,注意状态保存方法只有在Activity在异常销毁情况下才会调用,所以正常情况下是不能通过该方法来保存数据。onRestoreInstanceState一定会在onPostCreate之前调用,并在onStart之后调用,所以注意状态恢复后的UI初始化。如果是由于资源配置的变化引起的销毁重建,还会调用onConfigurationChanged方法,因此可在此方法中判断资源发生了什么变化。
3. ViewModel为何能在屏幕翻转Activity销毁后保存状态
当屏幕翻转时,Activity会调用onDestroy进行销毁,如果是正常情况下,ViewModel也应该被销毁,但是ViewModel却能够保存其状态,其本质是因为当Activity因资源配置变化重建时,ViewModel还是原来的ViewModel,并没有被销毁,接下来具体分析:
首先我们知道Activity实现了ViewModelStoreOwner接口,而从ViewModelStoreOwner中可以获得ViewModelStore,ViewModelStore内部存在一个HashMap,用来保存所创建的ViewModel。
创建ViewModel方式:
/**
* this表示Activity
*/
viewModel = ViewModelProvider(this)[ViewModel::class.java]
ViewModel的创建需要ViewModelStoreOwner,而Activity的父类继承了ViewModelStoreOwner接口,所以可以直接传入this,并且ViewModelProvider持有ViewModelStoreOwner的ViewModelStore,从以下代码可以验证:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
由于ViewModelProvider持有ViewModelStore,所以便可以创建新的ViewModel并保存到ViewModelStore的HashMap中,或者从中取出ViewModel,在创建ViewModel时,便是调用ViewModelStore的get方法来获取ViewModel,如果不存在,则创建:
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
到此,便产生了所需要的ViewModel。
当Activity销毁时:
由于Activity实现了LifycycleOwner接口,所以当Activity生命周期有所变化时,便会通知到所有注册的观察者:
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
if (Build.VERSION.SDK_INT >= 19) {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
Window window = getWindow();
final View decor = window != null ? window.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
}
}
});
}
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();
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
由上面代码可知,当Activity的生命周期状态为ON_DESTROY时,会首先判断是不是配置发生了改变,如果不是,则清空ViewModelStore中的HashMap。所以如果因为配置变化的原因导致Activity销毁,其ViewModel并没有销毁。那如果没有销毁,当Activity重新创建时怎么找到它的呢?
其实当Activity因为配置改变而需要重现创建时,Activity还会调用onRetainNonConfigurationInstance方法,该方法在onStop和onDestroy之间调用,并且接下来要立即创建新的Activity实例,就是在这里将ViewModelStore保存了起来:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
既然保存起来了,那我们怎么获取它的呢,其实答案就是在创建 ViewModel时来获取的,还记得在创建ViewModel时,ViewModelProvider持有了ViewModelStore,而这个ViewModelStore是从ViewModelStoreOwner的getViewModelStore方法得来的,所以肯定是Activity在重写此方法中做了处理:
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;
}
可以看到Activity首先是调用getlastNonConfigurationInstance方法来获取ViewModelStore,而该方法就是取出之前onRetainNonConfigurationInstance方法所保存的数据,所以就可以成功的找到了之前所创建的ViewModel,也就可以恢复之前的状态了。
总结:
ViewModel之所以能够屏幕翻转时保存状态,是因为在销毁之前保存了ViewModel(onRetainNonConfigurationInstance),并且在重新创建时取出之前的ViewModel(getViewModelStore)。onRetainNonConfigurationInstance方法只有在因为资源配置发生变化,Activity需要销毁重建时才会调用,所以如果是因为内存不足而销毁,则不会调用此方法,也就不能通过ViewModel的方式恢复之前的状态,此时可以调用onSaveInstanceState方法来进行恢复,所以调用onRetainnonConfigurationinstance方法的条件要比onSaveInstanceState苛刻的多。