ViewModel 是Jetpack Architecture 下 MVI/MVVM 架构的核心组件。今天来深入 ViewModel 的跨配置存活和生命周期的底层原理。
我们可以先定义一个继承自 ViewModel 的 MyViewModel 类:
java
public class MyViewModel extends ViewModel {
private final MutableLiveData<String> _userName = new MutableLiveData<>();
public MyViewModel() {
_userName.setValue("张三");
}
public LiveData<String> get_userName() {
return _userName;
}
public void update_userName(String newValue) {
_userName.setValue(newValue);
}
}
这次我们不使用 ViewModelProvider,而是直接实例化:
java
MyViewModel myVm = new MyViewModel();
然后订阅该状态:
java
myVm.get_userName().observe(this, newName -> tv.setText(newName));
// 或不采用 lambda
myVm.get_userName().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
tv.setText(s);
}
});
当你调用 update_userName() 将 _userName 的值更新为 "李四",Observer 观察到值发生改变,立即触发回调更新 TextView。但如果此时你旋转屏幕,你会发现 TextView 的值又变回了 "张三"。这是因为我们绕过了 ViewModelProvider,没有用到它背后的存储机制。严格意义上讲,ViewModel 这个类本身好像并没有保持状态的能力?
其实,它也没有 Lifecycle,不知道 Activity 何时重建,不具备任何跨配置变更存活的魔法。ViewModel 只是一个数据容器,负责把 UI 需要的数据从 Activity 中解耦。
保持状态的类是 ViewModelStore,所以正如 Android 开发者文档 About ViewModel 所说:
实例化 ViewModel 时,您会向其传递实现 ViewModelStoreOwner 接口的对象。它可能是 Navigation 目的地、Navigation 图表、activity 或实现接口的任何其他类型。您还可以使用 rememberViewModelStoreOwner API 将 ViewModel 直接限定到可组合项。然后,ViewModel 的作用域将限定为 Lifecycle 的 ViewModelStoreOwner。它会一直保留在内存中,直到其 ViewModelStoreOwner 永久消失 (例如,当可组合项所有者退出组合时)。
ViewModelStoreOwner 接口定义了 viewModelStore 变量:
kotlin
// ViewModelStoreOwner 源码
public interface ViewModelStoreOwner {
/** The owned [ViewModelStore] */
public val viewModelStore: ViewModelStore
}
我们使用 ViewModelProvider 创建 MyViewModel,传入当前 Activity,而 Activity 的基类 ComponentActivity 实现了 ViewModelStoreOwner 接口:
java
MyViewModel myVm;
@Override
protected void onCreate(Bundle saveInstanceState) {
myVm = new ViewModelProvider(this).get(MyViewModel.class);
}
ComponentActivity 对 viewModelStore 的实现如下:
kotlin
override val viewModelStore: ViewModelStore
get() {
checkNotNull(application) {
("Your activity is not yet attached to the " +
"Application instance. You can't request ViewModel before onCreate call.")
}
ensureViewModelStore()
return _viewModelStore!!
}
private fun ensureViewModelStore() {
if (_viewModelStore == null) {
val nc = lastNonConfigurationInstance as NonConfigurationInstances?
if (nc != null) {
// 从 NonConfigurationInstances 中恢复 ViewModelStore
_viewModelStore = nc.viewModelStore
}
if (_viewModelStore == null) {
_viewModelStore = ViewModelStore()
}
}
}
这里就是 ViewModel 旋转屏幕状态不丢失的核心实现。可以看到在 Activity 绑定之前使用 ViewModelProvider(this).get(MyViewModel.class) 会打印错误信息。
- 屏幕旋转后 Activity 重建,
_viewModelStore会被重置,但nc != null→ 旧的ViewModelStore从NonConfigurationInstances中恢复,里面保存的所有 ViewModel 实例都还在。 - 首次启动 Activity 时
nc == null,然后创建一个空的ViewModelStore,后续 ViewModel 会被创建并存入。
屏幕旋转时 Activity 会先销毁,系统在销毁前会调用 Activity.onRetainNonConfigurationInstance(),ComponentActivity 重写了该方法:
kotlin
@Suppress("deprecation")
final override fun onRetainNonConfigurationInstance(): Any? {
// Maintain backward compatibility.
val custom = onRetainCustomNonConfigurationInstance()
var viewModelStore = _viewModelStore
if (viewModelStore == null) {
// 没有调用过 getViewModelStore(),则尝试从上次的 NonConfigurationInstance 中获取
val nc = lastNonConfigurationInstance as NonConfigurationInstances?
if (nc != null) {
viewModelStore = nc.viewModelStore
}
}
if (viewModelStore == null && custom == null) {
return null
}
val nci = NonConfigurationInstances()
nci.custom = custom
nci.viewModelStore = viewModelStore
return nci
}
这个方法会先把当前的 _viewModelStore 取出;如果未创建过(即从未调用 getViewModelStore()),就会尝试从上次的 NonConfigurationInstances 里复用。最后把 viewModelStore 和自定义对象一起打包进新的 NonConfigurationInstances 对象中返回。这个返回的对象会被系统保留,在 Activity 重建后通过 getLastNonConfigurationInstance() 获取,也就是 ensureViewModelStore() 中看到的恢复来源。其中 lastNonConfigurationInstance 其实是 Kotlin 的语法糖,调用的是 Activity 的该方法:
kotlin
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
我们接着来看 ViewModelStore 是怎么持有这些 ViewModel 的,其实很简单,它内部维护了一个 HashMap:
kotlin
public open class ViewModelStore {
private val map = mutableMapOf<String, ViewModel>()
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun put(key: String, viewModel: ViewModel) {
val oldViewModel = map.put(key, viewModel)
oldViewModel?.clear()
}
/** Returns the `ViewModel` mapped to the given `key` or null if none exists. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public operator fun get(key: String): ViewModel? {
return map[key]
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun keys(): Set<String> {
return HashSet(map.keys)
}
/** Clears internal storage and notifies `ViewModel`s that they are no longer used. */
public fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}
}
现在我们知道了 ViewModel 只是一个数据容器,真正保证 ViewModel 跨配置存活的类是 ViewModelStore。我们接着来看 ViewModelProvider 是怎么通过 get() 拿到我们想要的 ViewModel 的。
来看源码,ViewModelProvider 采取了代理模式:
kotlin
public actual open class ViewModelProvider
private constructor(private val impl: ViewModelProviderImpl)
ViewModelProvider 的两种构造方法最终都会交给私有主构造器去创建 ViewModelProviderImpl:
kotlin
@JvmOverloads
public constructor(
store: ViewModelStore,
factory: Factory,
defaultCreationExtras: CreationExtras = CreationExtras.Empty,
) : this(ViewModelProviderImpl(store, factory, defaultCreationExtras))
public constructor(owner: ViewModelStoreOwner) : this(
store = owner.viewModelStore,
factory = ViewModelProviders.getDefaultFactory(owner),
defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner),
)
我们可以看到 ViewModelProvider 其实是把 get() 方法委托给 ViewModelProviderImpl 代理类 impl 去执行。
kotlin
@MainThread
public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
impl.getViewModel(modelClass)
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T = get(modelClass.kotlin)
@MainThread
public actual operator fun <T : ViewModel> get(key: String, modelClass: KClass<T>): T =
impl.getViewModel(modelClass, key)
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T =
impl.getViewModel(modelClass.kotlin, key)
我们再去看 ViewModelProviderImpl 的具体实现:
kotlin
internal class ViewModelProviderImpl(
private val store: ViewModelStore,
private val factory: ViewModelProvider.Factory,
private val defaultExtras: CreationExtras,
) {
private val lock = SynchronizedObject()
@Suppress("UNCHECKED_CAST")
internal fun <T : ViewModel> getViewModel(
modelClass: KClass<T>,
key: String = ViewModelProviders.getDefaultKey(modelClass),
): T {
return synchronized(lock) {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
if (factory is ViewModelProvider.OnRequeryFactory) {
factory.onRequery(viewModel!!)
}
return@synchronized viewModel as T
}
val modelExtras = MutableCreationExtras(defaultExtras)
modelExtras[ViewModelProvider.VIEW_MODEL_KEY] = key
return@synchronized createViewModel(factory, modelClass, modelExtras).also { vm ->
store.put(key, vm)
}
}
}
}
internal expect fun <VM : ViewModel> createViewModel(
factory: ViewModelProvider.Factory,
modelClass: KClass<VM>,
extras: CreationExtras,
): VM
这下明了了:第一次调用 ViewModelProvider(owner: ViewModelStoreOwner).get() 会执行 createViewModel()(不同平台有不同实现),并将创建的 ViewModel 存入 ViewModelStore;当配置变更再次调用 get() 时,直接从 store[key] 返回已有实例,彻底避免了数据丢失。
ViewModel 的生命周期
官方文档对 ViewModel 的生命周期是这样描述的:
ViewModel 的生命周期与其作用域直接关联。ViewModel 会一直保留在内存中,直到其作用域 ViewModelStoreOwner 消失。以下上下文中可能会发生这种情况:
- 对于 activity,是在 activity 完成时。
- 对于 Navigation 条目,是在 Navigation 条目从返回堆栈中移除时。
- 对于可组合项,是在可组合项退出组合时。您可以使用 rememberViewModelStoreOwner 将 ViewModel 直接限定到界面的任意部分(例如 Pager 或 LazyList)。
这使得 ViewModels 成为了存储在配置更改后仍然存在的数据的绝佳解决方案。
我们可以在 ComponentActivity 中找到对应的源码实现:
kotlin
lifecycle.addObserver(
LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
contextAwareHelper.clearAvailableContext()
// And clear the ViewModelStore
if (!isChangingConfigurations) {
viewModelStore.clear()
}
reportFullyDrawnExecutor.activityDestroyed()
}
}
)
可以看到,在当前 Activity 销毁时它会判断是否是配置发生改变导致的销毁。如果不是,则认为 Activity 是正常结束且不再需要,此时会调用 viewModelStore.clear() 方法,遍历 HashMap 并依次调用所有 ViewModel 的 clear() 方法执行收尾操作,最后调用 map.clear()(具体实现见前面贴出的 ViewModelStore 源码)。
我们再来看 ViewModel 的 clear() 方法:
kotlin
@MainThread
internal actual fun clear() {
impl?.clear()
onCleared()
}
ViewModel 实际上把一些资源的回收交给了代理类去实现,然后再去执行我们重写的 onCleared() 进行一些没有通过 ViewModelImpl 的 addCloseable() 方法挂载的资源的回收:
kotlin
@MainThread
fun clear() {
if (isCleared) return
isCleared = true
synchronized(lock) {
for (closeable in keyToCloseables.values) {
closeWithRuntimeException(closeable)
}
for (closeable in closeables) {
closeWithRuntimeException(closeable)
}
// 只清空无 key 的资源集合,防止 viewModelScope 等资源被意外重建
closeables.clear()
}
}
这个 ViewModelImpl.clear() 的核心工作就是关闭所有通过 addCloseable 添加的、实现了 AutoCloseable 接口的实例(例如 viewModelScope 的协程作用域,以及你手动添加的各种可关闭资源)。关闭顺序为先关有 key 的(keyToCloseables),再关无 key 的(closeables),最后只清空无 key 集合,避免 viewModelScope 等有 key 资源被意外重建。 这里附上ViewModel的生命周期的图, 其实就是上面提到的文档中的图, 点击阅读也能看到: 
至此,ViewModel 的整个生存之谜全部解开:
- ViewModel 本身只是普通数据容器,不具魔法;
- ViewModelStore 是持有 ViewModel 的 HashMap;
- NonConfigurationInstances 负责在配置变更时保存和恢复 ViewModelStore;
- ViewModelProvider 封装了"先从 Store 取,没有再创建"的逻辑;
- ComponentActivity 利用生命周期在真正销毁时清理 ViewModelStore,防止内存泄漏,而在配置变更时则保留 ViewModelStore,使数据得以存活。