ViewModel为什么可以保存数据?

1,853 阅读2分钟

基础知识

  • ViewModel通过ViewModelProvider类获取,先尝试从ViewModelStore获取ViewModel实例,如果没有,则使用Factory创建,然后存入ViewModelStore。
  • ViewModelStore是ViewModel存储器,内部通过LinkedHashMap存储ViewModel
  • ViewModelStoreOwner是ViewModel存储器拥有者
  • ViewModelStoreOwner是个接口,实现类有ComponentActivity和Fragment,也就是说ComponentActivity和Fragment 都是ViewModel存储器的拥有者
  • 屏幕旋转或配置发生变化会调用ComponentActivity的onRetainNonConfigurationInstance方法,在performDestroyActivity阶段执行
  • Activity对应的ActivityClientRecord变量不受Activity因配置变化而销毁重建的影响

源码分析

1.viewModel如何保存

我们从启动Activity开始,ComponentActivity构造函数中执行ViewModelStore的初始化: image.png image.png 其中getLastNonConfigurationInstance是Activity的方法 image.png image.png 可以看出mLastNonConfigurationInstances的赋值发生在Activity.attach方法内,而一开始lastNonConfigurationInstances是没有值的,为null,所以此刻getLastNonConfigurationInstance方法返回的是null,所以ensureViewModelStore方法内会执行:

if (mViewModelStore == null) {
    mViewModelStore = new ViewModelStore();
}

即ViewModelStore的创建!

接下来到我们使用ViewModel,有创建两种方式:
1.直接使用ViewModelProvider
ViewModelProvider(this).get(XXXViewModel::class.java)
2.使用第三方依赖注入库:koin
val XXXViewModel: XXXViewModel by viewModel()

不管是哪种,最终都会走到ViewModelProvider的get方法来获取ViewModel

image.png 那我们一开始ViewModelStore内肯定是不存在ViewModel的,则使用fatory创建一个,内部使用了反射,然后放入ViewModelStore,内部通过LinkedHashMap存储ViewModel,可以自己看下源码

好了,此时我们的ViewModelStore内部存储了ViewModel实例


接下来我们旋转屏幕,Aactivity1销毁,Aactivity2重建,Aactivity1销毁时,执行到performDestroyActivity时,会执行activity的retainNonConfigurationInstances方法

image.png image.png ComponentActivity重写了这个方法: image.png 这个方法很重要,我们一起来看下,这个时候,viewModelStore肯定不为null,所以两个if都不会执行,直接new一个NonConfigurationInstances出来,把当前的viewModelStore赋值给NonConfigurationInstances,其中viewModelStore有我们的viewModel!所以上上图中的activity局部变量是NonConfigurationInstances实例,所以咱们的viewModel一路传递到了performDestroyActivity 中的r.lastNonConfigurationInstances中,即ActivityClientRecord中!

我们可以得出结论:我们旋转屏幕,Aactivity1销毁时,会把我们的viewModel保存到ActivityClientRecord中,又因为ActivityClientRecord不受Activity因配置变化而销毁重建的影响,所以它能够帮我们安全得保留了数据

2.viewModel如何获取

我们在前面讲到,启动Activity时,ComponentActivity构造函数中执行了ViewModelStore的初始化: image.png image.png 此时,getLastNonConfigurationInstance会返回上次在ActivityClientRecord中保留的NonConfigurationInstances实例,里面存储着咱们的viewModelStore以及里面的viewModel,所以nc!=null,ViewModelStore直接被赋值,不会执行下面红框内的new初始化,为什么这次的getLastNonConfigurationInstance方法不是null,因为在Activity.attach方法中,我们对mLastNonConfigurationInstances赋值了,而这次赋值不再是null!


顺便给练习项目求个Star🌟🌟🌟,万分感谢🙏🙏🙏:
github.com/stewForAni/…

WechatIMG382.jpeg