作用
- 用来联系 View 和 model 之间的关系,存储和管理界面相关的数据
- ViewModel的生命周期跟随页面生命周期,页面销毁,自动清除ViewModel,防止内存泄漏
- 不会因为屏幕旋转而销毁,减少了维护状态的工作
主要的类
分析版本:2.3.1。
- ViewModelProvider:获取和管理 ViewModel 实例的类。
- ViewModelFactory:自定义创建 ViewModel 实例的工厂类。
- ViewModelStore:持有 ViewModel 实例的容器类。它在配置更改时(如屏幕旋转)存储和管理 ViewModel 实例,以便在恢复后重新获取相同的 ViewModel 实例。
- ViewModelStoreOwner:通常,Activity 和 Fragment 实现了 ViewModelStoreOwner 接口,因此它们可以存储 ViewModel 实例并与生命周期相关联。
创建过程
kotlin
class TestViewModel : ViewModel() {
val data = MutableLiveData<Int>(0)
override fun onCleared() {
super.onCleared()
}
}
//在Activity中使用,通过 by viewModels() 进行获取
val model: TestViewModel by viewModels()
或者
val viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
by viewModels()
- factoryProducer:用于实现自定义 ViewModelFactory,不传的话,默认为ComponentActivity#getDefaultViewModelProviderFactory。
- storeProducer:一般指的就是 Activity 或者 Fragment。
- 最后通过 ViewModelProvider#get 方法,获取到指定 Class 的 ViewModel 对象。
kotlin
@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
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
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
ViewModelStoreOwner
- ViewModelStoreOwner,一般指的就是 Activity 和 Fragment 了。
- 可以关注一下 ComponentActivity 里面的 getViewModelStore() 实现。
- 一般情况下,启动 Activity 之后,是通过 new 创建的 ViewModelStore。
- 比如屏幕旋转,数据恢复,当 getLastNonConfigurationInstance 不为空的时候,可以从 NonConfigurationInstances 中去读取的ViewModelStore 进行赋值。
ViewModelStore
- 逻辑比较简单,就是使用 Map,存储已创建的 ViewModel。
- 在 Activity 或者 Fragment 在销毁的时候,调用 clear 方法。
ViewModelProvider
- 一般情况下,都是通过 ViewModelProvider#get(java.lang.Class) 来获取 ViewModel。
- 不设置key值的话:key值,默认是 "androidx.lifecycle.ViewModelProvider.DefaultKey"+ ViewModel的类名。
- 通过 key 去 ViewModelStore 获取 ViewModel,不为空,直接返回。
- 若为空,通过 ViewModelFactory#create 方法进行创建,然后再放入 ViewModelStore。
- 另一种获取 ViewModel 的方法,ViewModelProvider#get(key, modelClass),可以配合 KeyedFactory 进行使用。可以在同一个Activity下,根据 key 进行区分,创建多个同种 Class 的 ViewModel。(后面再细讲)
恢复过程
- 保存 lastNonConfigurationInstance
- ActivityThread的performDestroyActivity 方法中,在 Activity 销毁的时候,将存有 viewModelStore 的NonConfigurationInstances 保存到了 ActivityClientRecord 中。
- handleRelaunchActivity 中通过获取刚刚的 ActivityClientRecord,再拿到 NonConfigurationInstances,最后可以拿到之前的 ViewModelStore。
- ViewModelStore 里面的 ViewModel对象,以及里面的各种数据,比如 LiveData 数据,都是之前存在的。所以直接 observe LiveData,就可以恢复 UI 了。
- 恢复lastNonConfigurationInstance
ViewModelScope 原理
- viewModelScope 创建的协程的生命周期和 viewModel 的生命周期是绑定的。当 viewModel 被取消的时候,viewModelScope 创建的协程也会被取消。
- 而 ViewModel 的生命周期和 Activity 或 fragment 绑定,所以在页面销毁的时候,我们就不需要手动去做一些释放资源的工作了。
- 创建 CoroutineScope ,并通过 setTagIfAbsent 方法,存到 ViewModel 里面的 mBagOfTags 里面。
- mBagOfTags 是一个 HashMap,可以用来存放一些数据。
- 当 ViewModel#clear 方法被执行的时候,如果是一个 Closeable 对象的话,会调用它的close方法。
- viewModelScope 是一个实现 Closeable 接口的 CoroutineScope,close 方法里面会取消协程。
ViewModel 泄露检测
这里分析一下 LeakCanary 里面的实现 ViewModelClearedWatcher。
- ViewModelClearedWatcher,本身是一个 ViewModel,通过反射可以获取到 ViewModelStore 里面的mMap,mMap存储了所有的 ViewModel 对象。
- 当 Activity 或者 Fragment onCreate 的时候,ViewModelClearedWatcher#install 方法被执行,创建 ViewModelClearedWatcher。
- 当 Activity或者 Fragment 被销毁的时候,ViewModelClearedWatcher#onCleared 方法被执行。
- 这个时候,通过 ReachabilityWatcher#expectWeaklyReachable,把 mMap 里面的所有 ViewModel 对象加入到 LeakCanary的检测队列里面。
kotlin
internal class ViewModelClearedWatcher(
storeOwner: ViewModelStoreOwner,
private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {
// We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,
// however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0
// does not have ViewModelStore#keys. All versions currently have the mMap field.
private val viewModelMap: Map<String, ViewModel>? = try {
val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
mMapField.isAccessible = true
@Suppress("UNCHECKED_CAST")
mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
} catch (ignored: Exception) {
null
}
override fun onCleared() {
viewModelMap?.values?.forEach { viewModel ->
reachabilityWatcher.expectWeaklyReachable(
viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
)
}
}
companion object {
fun install(
storeOwner: ViewModelStoreOwner,
reachabilityWatcher: ReachabilityWatcher
) {
val provider = ViewModelProvider(storeOwner, object : Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
})
provider.get(ViewModelClearedWatcher::class.java)
}
}
}
拆分 ViewModel
随着业务的迭代,页面的逻辑变得复杂,这里的 ViewModel 类代码会变复杂,变得臃肿。
需要考虑进行拆分 ViewModel,这里直接上之前的一篇文章。