ViewModel 本身只是一个数据容器——真正让它在屏幕旋转后存活的是 ViewModelStore,而 ComponentActivity 通过 NonConfigurationInstances 在重建时恢复整个 Store;其生命周期在 Activity 真正销毁时才结束
ViewModel 是 Jetpack 组件库 Architecture 分类下的一个组件,是 MVI 和 MVVM 架构的核心。先定义一个继承 ViewModel 的类:
public class MyViewModel extends ViewModel {
private final MutableLiveData<String> _userName = new MutableLiveData<>();
public MyViewModel() {
_userName.setValue("张三");
}
public LiveData<String> getUserName() {
return _userName;
}
public void updateUserName(String newValue) {
_userName.setValue(newValue);
}
}
这次我们不使用 ViewModelProvider,而是直接实例化并订阅:
MyViewModel myVm = new MyViewModel();
// 订阅状态变化
myVm.getUserName().observe(this, newName -> tv.setText(newName));
// 或不采用 lambda
myVm.getUserName().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
tv.setText(s);
}
});
调用 updateUserName("李四") 后,Observer 检测到值变化立即触发回调更新 TextView。但如果此时旋转屏幕,TextView 的值又变回了"张三"。
new MyViewModel() 重新执行,构造函数里又把 _userName 设为了 "张三"。ViewModel 类本身只是一个数据容器,不具备跨配置变更存活的魔法。
严格来说,ViewModel 这个类本身:
真正保持状态的类是 ViewModelStore。正如 Android 开发者文档 About ViewModel 所述:
实例化 ViewModel 时,您会向其传递实现 ViewModelStoreOwner 接口的对象。它可能是 Navigation 目的地、Navigation 图表、activity 或实现接口的任何其他类型。您还可以使用 rememberViewModelStoreOwner API 将 ViewModel 直接限定到可组合项。然后,ViewModel 的作用域将限定为 Lifecycle 的 ViewModelStoreOwner。它会一直保留在内存中,直到其 ViewModelStoreOwner 永久消失(例如,当可组合项所有者退出组合时)。
—— Android 开发者文档 · ViewModelViewModelStoreOwner 接口只定义了一个属性:
// ViewModelStoreOwner 源码
public interface ViewModelStoreOwner {
/** The owned [ViewModelStore] */
public val viewModelStore: ViewModelStore
}
我们使用 ViewModelProvider 时传入的 Activity 就是 ViewModelStoreOwner。Activity 的基类 ComponentActivity 实现了该接口:
MyViewModel myVm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// this = Activity = ViewModelStoreOwner
myVm = new ViewModelProvider(this).get(MyViewModel.class);
}
new ViewModelProvider(this).get(...),ComponentActivity 会抛出异常:"Your activity is not yet attached to the Application instance."。很多 Android 初学者都见过这个错误,看源码后就能释怀了。
ComponentActivity 对 viewModelStore 的 getter 实现如下:
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 会先销毁,系统在销毁前会调用 Activity.onRetainNonConfigurationInstance()。ComponentActivity 重写了该方法:
@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 的该方法:
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
ViewModelStore 的内部实现非常简单,核心就是一个 HashMap:
public open class ViewModelStore {
private val map = mutableMapOf<String, ViewModel>()
fun put(key: String, viewModel: ViewModel) {
val oldViewModel = map.put(key, viewModel)
oldViewModel?.clear()
}
operator fun get(key: String): ViewModel? {
return map[key]
}
fun keys(): Set<String> {
return HashSet(map.keys)
}
fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}
}
所以所谓的"跨配置存活"本质上就是:一个 HashMap 被藏在 NonConfigurationInstances 里,Activity 重建时把它找回来。毫不神秘。
ViewModelProvider 采用了经典的代理模式:
public actual open class ViewModelProvider
private constructor(private val impl: ViewModelProviderImpl)
两种构造方法最终都交给私有主构造器去创建 ViewModelProviderImpl:
@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),
)
get() 方法直接委托给 impl:
@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 中:
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
用流程图总结:
官方文档对 ViewModel 的生命周期是这样描述的:
ViewModel 的生命周期与其作用域直接关联。ViewModel 会一直保留在内存中,直到其作用域 ViewModelStoreOwner 消失。以下上下文中可能会发生这种情况:对于 activity,是在 activity 完成时;对于 Navigation 条目,是在 Navigation 条目从返回堆栈中移除时;对于可组合项,是在可组合项退出组合时。
这使得 ViewModels 成为了存储在配置更改后仍然存在的数据的绝佳解决方案。
我们可以在 ComponentActivity 中找到对应的源码实现:
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()
}
}
)
关键判断:if (!isChangingConfigurations)。在当前 Activity 销毁时,它会判断是否是配置发生改变导致的销毁。如果不是(即 Activity 是正常结束且不再需要),才调用 viewModelStore.clear() 方法清理所有 ViewModel。
isChangingConfigurations 判断负责"不清理"。保存 + 不清理 = 跨配置存活。
ViewModelStore.clear() 会遍历 HashMap 并依次调用所有 ViewModel 的 clear() 方法。我们来看 ViewModel 的 clear() 方法:
@MainThread
internal actual fun clear() {
impl?.clear()
onCleared()
}
ViewModel 先把一些资源的回收交给代理类 ViewModelImpl 去实现,然后再执行我们重写的 onCleared() 进行自定义资源的回收:
@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 | 数据容器,解耦 UI 数据 | 自身无存活能力 |
| ViewModelStore | HashMap 仓库,持有 ViewModel | 跨配置存活的容器 |
| ViewModelStoreOwner | 对外暴露 viewModelStore | 接口,ComponentActivity 实现 |
| NonConfigurationInstances | Activity 重建时保留对象 | onRetainNonConfigurationInstance 保存,getLastNonConfigurationInstance 恢复 |
| ViewModelProvider | 门面,代理给 ViewModelProviderImpl | 代理模式 |
| ViewModelProviderImpl | 先查后创 + 线程安全 | synchronized + store 读写 |
| ON_DESTROY 观察者 | 判断是否因配置变更销毁 | isChangingConfigurations → 不清理 Store |
| ViewModelImpl.clear() | 关闭 addCloseable 资源 | viewModelScope、自定义 AutoCloseable |
isChangingConfigurations——如果是配置变更,不清理 ViewModelStore;如果是真正销毁,才调用 clear() 释放所有资源。ViewModel 只是包装盒,ViewModelStore 才是保管箱——后者依靠 onRetainNonConfigurationInstance 在 Activity 销毁前保存、ON_DESTROY 的 isChangingConfigurations 判断避免清理、getLastNonConfigurationInstance 在新 Activity 中恢复,完成了一个完整的跨配置存活闭环。