众所周知,ViewModel可以在界面销毁重建后仍然保存之前的数据,而到底是怎么在界面销毁重建期间进行保存的呢,本篇文章就就该问题进行一个探究。
由于ViewModel能够在界面销毁重建时保存数据,那我们就从Activity销毁的时机作为入口一探究竟。
AMS通知ApplicationThread执行界面销毁应用执行界面销毁是通过AMS通过Binder跨进程通知应用这边的ApplicationThread这个Binder对象,而ApplicationThread通过Handler最终会执行到ActivityThread.handleDestroyActivity()方法。
而这个方法又会调用performDestroyActivity()方法,我们看下源码:

Activity.retainNonConfigurationInstances()瞧一瞧NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
//...
return nci;
}
紧接着就会调用onRetainNonConfigurationInstance()方法,ComponentActivity会对这个方法进行重写:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
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;
}
这个方法值得细细分析一下,首先先搞清楚mViewModelStore是个啥:

看到这里是不是明白了:mViewModelStore是个ViewModelStore类型,而我们在Activity界面创建的ViewModel就会保存到这个对象之中。
到了这里,我们就可以知道:ActivityThread.handleDestroyActivity()最终会一步步走到mViewModelStore,将其进行保存。
接下来我们就看下这个值mViewModelStore经过一步步调用是怎么保存的 。
mViewModelStore如何一步步调用保存?回到我们的方法onRetainNonConfigurationInstance()中,从源码中可以看到,mViewModelStore最终会保存到ComponentActivity$NonConfigurationInstances类的viewModelStore成员属性中,并返回ComponentActivity$NonConfigurationInstances对象。
简单看下NonConfigurationInstances类结构:
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
我们跳到调用onRetainNonConfigurationInstance()方法的Activity.retainNonConfigurationInstances()方法中:

可以看到上面的onRetainNonConfigurationInstance()方法返回的ComponentActivity$NonConfigurationInstances对象最终会保存到Activity$NonConfigurationInstances类的activity成员变量中。
请注意,别搞混了NonConfigurationInstances类,Acitivity和ComponentActivity都有定义这个类,我们看下Acitivity定义的NonConfigurationInstances结构:
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
最终Activity.retainNonConfigurationInstances()方法会将Activity$NonConfigurationInstances类对象返回到上一层。
最终我们又回到了performDestroyActivity()方法中:

最终Activity$NonConfigurationInstances会保存到ActivityClientRecord的lastNonConfigurationInstances属性中。
而这个ActivityClientRecord是保存到ActivityThread的mActivities集合中,其中key就是token,value就是为ActivityClientRecord。
界面重新创建销毁后,AMS会通知ApplicationThread最终调用到performLaunchActivity()方法。
这个方法就是用来创建Activity的,创建完毕后就会调用我们熟悉的Activity.attach()方法:

可以看到这个方法传递的参数其中之一就是ActivityClientRecord的lastNonConfigurationInstances属性,继续深入看下Activity.attach()方法:
final void attach(//...NonConfigurationInstances lastNonConfigurationInstances,//...) {
attachBaseContext(context);
//...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
//...
}
最终这个lastNonConfigurationInstances会赋值给Acitivty的mLastNonConfigurationInstances属性。
而mLastNonConfigurationInstances就是Activity$NonConfigurationInstances对象,就保存了之前存储ViewModel的ViewModelStore,这就间接实现了保存了ViewModel中持有的数据。
ViewModel的获取流程我们看下如何在Activity中创建一个ViewModel:
private val mViewModel: MainViewModel by viewModels()
关键就是viewModels()方法:

最终ViewModel会尝试从viewModelStore中获取,获取不到通过反射创建。而viewModelStore是从哪里来的呢?



可以看到,最终这个viewModelStore最终就是从上面的Activity.mLastNonConfigurationInstances属性中获取。
本篇文章我们详细分析ViewModel如何实现在Activity界面销毁重建后还能够保存销毁前的数据的,希望对你有所帮助。
阅读量:1089
点赞量:0
收藏量:0