持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
众所周知,
ViewModel可以在界面销毁重建后仍然保存之前的数据,而到底是怎么在界面销毁重建期间进行保存的呢,本篇文章就就该问题进行一个探究。
由于ViewModel能够在界面销毁重建时保存数据,那我们就从Activity销毁的时机作为入口一探究竟。
AMS通知ApplicationThread执行界面销毁
应用执行界面销毁是通过AMS通过Binder跨进程通知应用这边的ApplicationThread这个Binder对象,而ApplicationThread通过Handler最终会执行到ActivityThread.handleDestroyActivity()方法。
而这个方法又会调用performDestroyActivity()方法,我们看下源码:
-
retainNonConfigurationInstances()就是ViewModel能在界面销毁重建后保存数据的关键方法,稍后会进行详细分析; -
Instrumentation.callActivityOnDestroy()这个方法最终就会回调大家熟悉的Activity.onDestroy()方法;
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界面销毁重建后还能够保存销毁前的数据的,希望对你有所帮助。