ViewModel的实例化方法有很多,例如在Activity里可以使用ViewModelProvider实例化一个对象:
private val vm:ViewModel by lazy {
ViewModelProvider(this)[ViewModel::class.java]
}
也可以更简单一点用委托属性:
private val vm :ViewModel by viewModels()
在Compose里还可以这样:
val model: ViewModel = viewModel()
对于复杂一点需要提供Factory的情况:
val model: ViewModel = viewModel(
factory = object : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ViewModel::class.java)) {
return ViewModel() as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
)
那么对于上述情况,ViewModel是如何具体实例化和跟随Activity的生命周期的呢?
一、相关类介绍
1、基本类
| 类名 | 介绍 |
|---|---|
| ViewModel | 抽象类,调用clear方法销毁资源,销毁前回调onCleared方法 |
| ViewModelStore | 利用map存储ViewModel对象,clear方法:遍历存储的ViewModel对象调用其clear方法 |
| ViewModelStoreOwner | 接口,需要实例化一个ViewModelStore对象 |
| ViewModelProvider | 生成ViewModel对象的中间人 |
| Factory | 需实现create方法实例化一个ViewModel对象 |
| HasDefaultViewModelProviderFactory | 接口,需要实例化一个Factory对象 |
2、衍生类
| 类名 | 介绍 |
|---|---|
| ComponentActivity | 实现了ViewModelStoreOwner、HasDefaultViewModelProviderFactory接口 |
| AndroidViewModel | ViewModel子类,包含Application参数 |
| NewInstanceFactory | Factory的实现类,create方法通过无参构造函数实例化一个ViewModel对象,在它的伴生对象里有个属性instance,是NewInstanceFactory的对象,可以通过类名调用 |
| AndroidViewModelFactory | NewInstanceFactory的子类,需要Application来实例化一个AndroidViewModel对象 |
| SavedStateViewModelFactory | Factory的实现类,内部又维护一个Factory对象factory |
3、Compose相关
| 类名 | 介绍 |
|---|---|
| NavBackStackEntry | 类似于ComponentActivity,实现了ViewModelStoreOwner、HasDefaultViewModelProviderFactory接口,在NavHost中,NavBackStackEntry对象与页面一一对应,一个页面对应一个NavBackStackEntry对象,并且一个页面拥有唯一id |
| NavViewModelStoreProvider | 接口,需要实现getViewModelStore方法,根据id返回一个ViewModelStore实例 |
| NavControllerViewModel | NavViewModelStoreProvider的实现类,同时继承ViewModel,维护一个map保存ViewModelStore,根据id从map中得到ViewModelStore实例,如果不存在就实例化一个 |
| NavController | 维护返回栈和viewModel等相关参数,返回栈:栈内为NavBackStackEntry实例,viewModel:NavControllerViewModel实例 |
二、ViewModelStore和Factory来源
1、ViewModelStore来源
首先,ComponentActivity实现了ViewModelStoreOwner接口,因此它需要提供一个ViewModelStore
对于mViewModelStore
源头是直接实例了一个ViewModelStore对象,但后面分析可以得知它并不是每次都靠实例化一个对象。
2、Factory的来源
首先,ComponentActivity实现了HasDefaultViewModelProviderFactory接口,因此它需要提供一个Factory:
可以看到,提供的Factory是SavedStateViewModelFactory对象,在它的构造函数里,对它维护的Factory对象factory进行初始化
如果传入了application,就用getInstance方法,利用AndroidViewModelFactory的有参构造实例化,否则就用无参构造,反正都是AndroidViewModelFactory对象
所以到这里我们就知道了,SavedStateViewModelFactory想要实例化一个ViewModel有三个选择
- 自身create方法,因为它实现了Factory接口
- 内部属性factory,利用AndroidViewModelFactory去实例化
- 利用NewInstanceFactory伴生对象里的属性instance,它是NewInstanceFactory的实例,可以通过类名直接调用
外部不管那么多,具体的选择肯定在它的create方法里,来看相关代码:
findMatchingConstructor方法是查找是否有包含特定参数的构造方法,所以上面代码可以概况为:
如果ViewModel没有特定参数的构造方法,使用factory来实例化,否则就调用相关的构造方法传入相关参数来实例化。
由于factory是AndroidViewModelFactor的对象,因此再来看AndroidViewModelFactor的create方法
它的操作就是,如果包含application,就实例化为AndroidViewModel的对象,否则就调用父类的create方法,它的父类是NewInstanceFactory,NewInstanceFactor的create方法是通过反射调用无参构造实例化的。
总的概况就是:SavedStateViewModelFactory负责实例化特定参数的ViewModel,否则交给AndroidViewModelFactory,而AndroidViewModelFactory负责实例化AndroidViewModel对象,否则交给NewInstanceFactory,NewInstanceFactory通过无参构造实例化。
三、各情形下的实例化
1、通过ViewModelProvider
private val vm:ViewModel by lazy {
ViewModelProvider(this)[ViewModel::class.java]
}
短短的代码包含了两个过程
①实例化ViewModelProvider
它的构造方法是这样的
ComponentActivity实现了ViewModelStoreOwner接口,所以可以传入this,它也实现了HasDefaultViewModelProviderFactory接口,所以可以拿到Factory实例,调用另一个构造函数
②调用get方法
首先检查是不是本地类和匿名类,它们是不能创建ViewModel对象的,如果不是调用重载方法
store就是构造函数里的那个,它用map存储着ViewModel对象,所以上述代码可以概况为:
如果store里有就返回,否则就新建一个并存入store里
2、通过委托属性by viewModels()
private val vm :ViewModel by viewModels()
可以看到viewModels()其实是ComponentActivity的扩展方法,factory依旧是那个factory,viewModelStore依旧是那个viewModelStore,只是因为这是ComponentActivity的扩展方法,所以拿的更轻松一点。
继续看ViewModelLazy
所以它其实就是实现了对ViewModel的懒加载,其他都一样。
4、在Compose中使用
val model: ViewModel = viewModel()
这里面涉及内容很多,逐步分析
①非NavHost里获取ViewModelStore、Factory
在ComponentActivity里可以看到这一段代码
进入其中的一个set方法
发现它把ViewModelStoreOwner存到了根View里
然后再看Compose的LocalViewModelStoreOwner.current
发现如果LocalViewModelStoreOwner.current有值就返回,否则就把保存在view里的ViewModelStoreOwner拿出来,有了ViewModelStoreOwner调用其方法就可以得到ViewModelStore
既然ViewModelStore来自ComponentActivity,那么还可以得到ComponentActivity的Factory,这里不再赘述。
②NavHost里获取ViewModelStore、Factory
我们知道,使用NavHost需要提供NavController对象,在NavHost里,调用了NavController的setViewModelStore方法
在这里,如果没有嵌套NavHost的话,代码第一行得到的viewModelStoreOwner的值就是上一节提到的保存在View中的ViewModelStoreOwner对象。
深入看setViewModelStore方法
可以知道NavController维护了一个ViewModel实例,并且它是NavControllerViewModel.getInstance()方法得到的实例,继续深入
原来它是NavControllerViewModel对象,NavControllerViewModel在前面介绍过,它是NavViewModelStoreProvider接口的实现类,同时继承ViewModel,维护一个map保存ViewModelStore对象,所以在NavHost里实例化和存储ViewModelStore都在这里面
它实现的getViewModelStore方法就是,根据id从map中得到ViewModelStore实例,如果不存在就实例化一个
目前知道了ViewModelStore的创建和存储的地方,那么如何获取的呢?
前面说过,NavHost里的一个页面对应一个NavBackStackEntry实例,一个NavBackStackEntry又对应一个id,NavBackStackEntry的实例是在NavController里创建的,来看的NavBackStackEntry的create方法
重点是NavViewModelStoreProvider参数,NavController在调用这个方法时会传入它维护的NavControllerViewModel实例viewModel(NavViewModelStoreProvider是接口,NavControllerViewModel实现了它)
由于NavViewModelStoreProvider实现了ViewModelStoreOwner、HasDefaultViewModelProviderFactory接口,所以他需要和ComponentAvtivity一样提供Factory和ViewModelStore
其中viewModelStoreProvider就是构造函数里传进来的NavViewModelStoreProvider对象,调用它的方法传入唯一id,返回一个ViewModelStore对象,它具体的实现上面分析过。
再看Factory
factory还是一样的用的SavedStateViewModelFactory,直接懒加载实例化一个。
总结就是:首先利用NavHost外部的ViewModelStore创建存储一个NavControllerViewModel实例,这个实例的作用就是用map存储ViewModelStore对象,这个map的key就是每个页面的id,由此实现了一个页面一个ViewModelStore。如果没有嵌套NavHost,外部的ViewModelStore就是ComponentActivity的ViewModelStore,否则就是发生嵌套的那个页面的ViewModelStore
有了ViewModelStore再来具体谈谈实例化ViewModel
③实例ViewModel
在NavHost外面,
LocalViewModelStoreOwner.current前面分析过,NavHost外面,拿的是存储的View里的,如果是NavHost里,每个页面的ViewModelStoreOwner都不同,那么如何绑定页面,在不同页面获取到不同的ViewModelStoreOwner的呢?
在NavHost可以看到这样的代码
点进去
原来这是NavBackStackEntry的扩展方法,NavHost会将当前页面的ViewModelStoreOwner赋值给LocalViewModelStoreOwner.current,所以在不同页面获得的ViewModelStoreOwner不同。
四、生命周期管理
ComponentActivity重写了onRetainNonConfigurationInstance()方法,这个方法当Activity由于配置更改而被销毁并重新创建时被调用,可以将需要存储的数据返回。
而getLastNonConfigurationInstance()是获取存储的数据
首先看onRetainNonConfigurationInstance()方法
mViewModelStore是ComponentActivity维护的ViewModelStore对象,可以看到,如果它为null,就拿上次保存的数据并赋值然后进行返回存储。
在mViewModelStore被使用的时候,会先调用ensureViewModelStore方法,对mViewModelStore进行赋值,可以看到,这里也会调用getLastNonConfigurationInstance()方法,如果上次没有数据保存,然后才通过构造方法实例化。
从这里就可以得出,ViewModel为什么可以保存数据并且Activity非主动销毁时也不会丢,因此ViewModelStore实现了复用,在销毁时存储,在使用时恢复。
ComponentActivity还监测了生命周期
当不是因配置发生变化而引起Destory时,调用ViewModelStore的clear方法。
ViewModelStore的clear方法遍历存储的VieModel,调用其clear方法。
在Compose里依旧如此,各NavHost页面的ViewModelStore存储在NavControllerViewModel里,而NavControllerViewModel存储在ComponentActivity的ViewModelStore里,因此都处在一条链上,都能够与Activity的生命周期保持联动。