Viewmodel源码解析
现在Viewmodel相信大家都比较熟悉了,adnroid的官网上也介绍了它的一些特性。具体的这里就不多讲了。今天就来讲解它的源码,讲解为什么它能够在配置发生变化的时候还能保存数据。
现在在Activity和Fragment中怎么去初始化一个viewmodel呢?一般来讲就是通过谷歌给我们提供的扩展函数去初始化,具体如下
val plantingsViewModel:PlantListViewModel by viewModels()
所以今天,我就从这一个函数开始入手去看看Viewmodel内部是怎么实现的。
ActivityViewModelLazy.kt
//ActivityViewModelLazy.kt
@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory //ViewModelProvider.Factory类型
}
//viewModelStore是ViewModelStore类型
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
ViewModelProvider#ViewModelLazy
public class ViewModelLazy<VM : ViewModel> (
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
//获取ViewModel
get() {
//将cache赋值给Viewmodel
val viewModel = cached
//如果viewmodel为空,那么就通过 ViewModelProvider.get方法返回viewmodel,并将cache赋值
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
//如果有viewmodel不为空,那么直接放回
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
从这里的源码可以看出,这里面涉及的类有以下四个类
ViewModel
:viewmodel本身
ViewModelStore
:就是用来保存ViewModel
的
ViewModelProvider.Factory
:Factory 接口的实现负责实例化 ViewModel
。
ViewModelProvider
:为外部提供 创建ViewModel
的方法。
看来上面的代码流程,我们知道下一步的计划就是去看ViewModelProvider.get方法
ViewModelProvider
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
//真正获取viewmodel的地方
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//首先会通过key去ViewModelStore里面去看有没有viewmodel,这个key其实也就是你的viewModelClass相关的一些名字的拼接
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 {
//当缓存中没有时,就调用Factory的create方法创建ViewModel对象
viewModel = mFactory.create(modelClass);
}
//将ViewModel放入ViewModelStore缓存
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
看到这是不是有点缓存内味,先从缓存里拿数据,如果有数据就返回,如果缓存里没有就重新创建数据,并且将数据put到缓存中。所以要了解ViewModelStore
中取出来的viewmodel
为什么不变,那么就需要去看看ViewModelStore
是怎么创建的了。因为这里我们发现mViewModelStore
其实并不是静态变量,如果activity重建的话,那么mViewModelStore
应该也会重建。所以我们需要知道mViewModelStore
是怎么创建出来的。
这时候就需要回到getViewModelStore()
函数了
ComponentActivity#getViewModelStore
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
void ensureViewModelStore() {
if (mViewModelStore == null) {
//可以看到一开始在mViewModelStore等于null时,则先获取了NonConfigurationInstances对象
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
NonConfigurationInstances
是用来存储ViewModelStore
的一个类。当NonConfigurationInstances
对象不为null时,先直接从NonConfigurationInstances
拿ViewModelStore
,如果拿不到则就直接new
一个ViewModelStore
对象。这就是获取ViewModelStore
的过程。这里就是关键了,是不是重建之后的activity获取到的ViewModelStore
是从这里获取的呢?
这里有一个NonConfigurationInstances ,其对象是由调用getLastNonConfigurationInstance()
获取,而getLastNonConfigurationInstance
所返回的实例是由在onRetainNonConfigurationInstance
中保存的。
ComponentActivity#onRetainNonConfigurationInstance
//Retain all appropriate non-config state. You can NOT override this yourself! Use a androidx.lifecycle.ViewModel if you want to retain your own non config state.
//保留所有适当的非配置状态。你不能自己覆盖它!如果您想保留自己的非配置状态,请使用 androidx.lifecycle.ViewModel。
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
//如果 Activity 在第一次被重建后还未调用过 getViewModelStore() 方法
//此时 mViewModelStore 就还是为 null
//之后又发生了第二次重建,那就主动调用 getLastNonConfigurationInstance() 来获取第一次重建时保存的 ViewModelStore 实例
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
//将viewModelStore存储到NonConfigurationInstances中
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
onRetainNonConfigurationInstance
是在一些配置发生改变的时候调用的,比如横竖屏切换的时候。这样一来在屏幕切换前,先把viewModelStore
存储起来,然后屏幕切换后,再把viewModelStore
拿出来,这样,这个viewModelStore
相当于没有发生变化,从而里面的viewmodel也得以保存。
好到这里是不是看的有一知半解了,事实上到现在这里还有没有完全走完。因为我们还不知道NonConfigurationInstances
是什么时候被赋值的?这里面调用就会比较深层次了。这里我简单做一个总结吧:当屏幕发生旋转或其他配置发生变化的时候,ActivityThread
会调用performDestroyActivity
,而这个方法里面会调用Activity
的onDestory
,在performDestroyActivity
里面去调用onRetainNonConfigurationInstance
这时候,就在onDestory
之前把NonConfigurationInstances
给保存起来了,那么在重建Activty的时候,也就是Activity的attach函数的时候,再将这个保存起来的NonConfigurationInstances
传给Activity,这个时候就将这个NonConfigurationInstances
传递给新创建的Activity了。
以上就是为什么viewmodel能在屏幕发生旋转的时候,里面的viewmodel能保存的原因了。
那么总结一下ViewModel能保存数据的原因:
首先,第一次进入Activity时,由于ViewModel什么的都没有初始化,这时候,会通过ViewModelProvider相关的类来创建viewmodel,并且将这个viewmodel保存在ViewModelStore里面的HashMap中
当手机配置发生改变之后,会触发onRetainNonConfigurationInstance,对ViewModelStore进行存储,存储再NonConfigurationInstances里面
Activity重建后,Activity会重新走onAttach生命周期这时候,这时候将上次保存的NonConfigurationInstances传递给新的Activity。这时候Activity会重新走onCreate生命周期,并且会再次去获取ViewModel对象。
但是这次ViewModel的获取与第一次创建不同,它会通过ViewModelStoreOwner先获取该Activity重建之前所保存的ViewModelStore,这时候的ViewModelStore是在NonConfigurationInstances之中的,接着在ViewModelStore中根据Key,找到重建之前的ViewModel,使得这两次viewmodel数据一样