查看原文
其他

ViewModel为什么可以保存数据?

CoolStew 郭霖
2024-07-22


/   今日科技快讯   /

近日,比亚迪子公司弗迪电池已在今年3月与特斯拉达成上海储能工厂的供货协议,将于明年一季度向特斯拉供应储能电芯。该工厂的电芯一供已确定为宁德时代,二供为弗迪,后者的供应份额超过20%。


/   作者简介   /

本篇文章来自CoolStew的投稿,文章主要分享了为什么ViewModel可以保存数据,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。

CoolStew的博客地址:
https://juejin.cn/user/1627723012245054/posts

/   基础知识   /

ViewModel通过ViewModelProvider类获取,先尝试从ViewModelStore获取ViewModel实例,如果没有,则使用Factory创建,然后存入ViewModelStore。

ViewModelStore是ViewModel存储器,内部通过LinkedHashMap存储ViewModel。

ViewModelStoreOwner是ViewModel存储器拥有者。ViewModelStoreOwner是个接口,实现类有ComponentActivity和Fragment,也就是说ComponentActivity和Fragment 都是ViewModel存储器的拥有者。屏幕旋转或配置发生变化会调用ComponentActivity的onRetainNonConfigurationInstance方法,在performDestroyActivity阶段执行。

Activity对应的ActivityClientRecord变量不受Activity因配置变化而销毁重建的影响。

/   源码分析   /

ViewModel如何保存


我们从启动Activity开始,ComponentActivity构造函数中执行ViewModelStore的初始化:




其中getLastNonConfigurationInstance是Activity的方法。



可以看出mLastNonConfigurationInstances的赋值发生在Activity.attach方法内,而一开始lastNonConfigurationInstances是没有值的,为null,所以此刻getLastNonConfigurationInstance方法返回的是null,所以ensureViewModelStore方法内会执行:

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

即ViewModelStore的创建!

接下来到我们使用ViewModel,有创建两种方式:

直接使用ViewModelProvide

ViewModelProvider(this).get(XXXViewModel::class.java)

使用第三方依赖注入库:koin

val XXXViewModel: XXXViewModel by viewModel()

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


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

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

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



ComponentActivity重写了这个方法:


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

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


viewModel如何获取


我们在前面讲到,启动Activity时,ComponentActivity构造函数中执行了ViewModelStore的初始化:



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

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

在Android地图上实现一个雨雪特效吧

原创:写给初学者的Jetpack Compose教程,用derivedStateOf提升性能


欢迎关注我的公众号
学习技术或投稿


长按上图,识别图中二维码即可关注
继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存