学习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苛刻的多。