ViewModel 是 android 新的 mvvm 框架的一部分,它的出现就是为了解决以上两个场景中数据与 Controller 耦合过度的问题。其 基本原理 是:维护一个与配置无关的对象,该对象可存储 Controller 中需要的任何数据,其生命周期与宿主 Controller 的生命周期保持一致,不因 Controller 的重建而失效(注意:Controller 的重建仍然在 Controller 生命周期内,并不会产生一个新的生命周期,即 Controller 的 onDestroy 并不会调用)
这意味着无论是转屏还是系统字体变化等因配置变化产生的 Controller 重建都不会回收 ViewModel 中维护的数据,重建的 Controller 仍然可以从同一个 ViewModel 中通过获取数据恢复状态。
如果大家去看一下 ViewModel 类的实现,会发现虽然它是一个 abstract 类,但是没有暴露任何外部可访问的方法,其预留的方法都是 package 访问权限的, 其预留了一些数据清理工作的功能,推测可能是系统保留用作以后扩展,因为与我们对 ViewModel 原理的理解没有什么关联,我们暂且略过。
我们用一个结构图来剖析 ViewModel 的构造过程:
如图所示:
所有已经实例化的 ViewModel 都缓存在一个叫做 ViewModelStore 的封装对象中,其实质是一个 HashMap;
ViewModelStore 与具体的 Controller 绑定,并与宿主 Controller 俱生俱灭,所以这就解释了为何 ViewModel 与宿主 Controller 的生命周期是一样长了,因为缓存它的 ViewModelStore 与宿主 Controller 寿命相等;
获取 ViewModel 实例的过程委托给了一个叫做 ViewModelProvider 的工具类,它包含一个创建 ViewModel 的工厂类 Factory 和一个对 ViewModelStore 的引用;
总的构造过程为:先从 ViewModelStore 中获取缓存的 ViewModel,若没有缓存过则用 Facotry 实例化一个新的 ViewModel 并缓存,具体的过程分为 4 步,具体可参考图示。
本小节剩下部分分析源码,对于只关心原理的同学此部分可以略过:
我们在获取 ViewModel 的时候,一般通过如下方式:
// 在 Controller(这里以 Fragment 为例)的 onCreate 方法中调用
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
我们看一下 ViewModelProviders.of() 的实现:
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
// 最终用宿主 Controller 的 ViewModelStore 和一个 Factory 实例化一个
// ViewModelProvider
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
我们再看一下 ViewModelProvider.get() 方法获取 ViewModel 实例的过程:
public T get(@NonNull Class modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException(“Local and anonymous classes can not be ViewModels”);
}
// 我们看到了 ViewModel 在 ViewModelStore 中的 key 表示
return get(DEFAULT_KEY + “:” + canonicalName, modelClass);
}
public T get(@NonNull String key, @NonNull Class modelClass) {
// 先检查缓存中是否存在
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// 缓存中没有,通过 Factory 构造
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 新实例保存缓存
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
上一节我们说到,ViewModel 之所以能够与宿主 Controller 保持生命周期一致,是因为存储它的 ViewModelStore 与宿主 Controller 生命周期一致。那么为什么 ViewModelStore 能够保持和 Controller 生命周期一致呢?
这里我们需要先理清 FragmentActivity 和其寄生的 Fragment 的 ViewModelStore 之间的关系:
如图所示:
每个 ViewModelStore 依附于其宿主 Controller,所以各个 Controller 的 ViewModelStore 组成一个树状的引用关系;
处于顶层的 ViewModelStore 依附于 FragmentActivity,它除了保存用户级的 ViewModel 以外,还保存其儿子 Fragment 的 FragmentManagerViewModel;
FragmentManagerViewModel 主要维护两个对象:所属 Fragment 的 ViewModelStore 和其儿子 Fragment 的 FragmentManagerViewModel 的引用,注意图中的红色部分,所有二级及以下的子孙 Fragment 都共用同一个父节点的 Child FragmentManagerModel,这样当父 Fragment 销毁的时候方便一次性清除其所有子 Fragment 共用的 FragmentManagerViewModel;
但是二级及以下的子孙 Fragment 的 ViewModelStore 都是独立的,一个 Fragment 自身的 ViewModel 变化应该不影响其兄弟节点的 ViewModel,所以可以推导出,它们共同的 FragmentManager
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
ViewModel 应该是维护了一个保存各个子 Fragment 的 ViewModelStore 的容器,大家如果细看 FragmentManagerViewModel 的源代码,实际上就是这么做的。
所以,我们看到,处于顶层的 FragmentActivity 的 ViewModelStore 是一个超级 Store,它引用了所有的 ViewModels,包括自身的数据、所有子孙 Fragment 的 ViewModels,只要各子孙 Fragment 不清除自有 ViewModelStore,则所有的数据都维护在这棵 ViewModelStore 树中。
那么在配置发生变化的时候,ViewModelStore 树如何保持不变呢?
将 ViewModelStore 作为配置无关数据进行保持,在 FragmentActivity 中是这么做的:
是的,流程就是这么简单,只需要将 ViewModelStore 封装在一个特殊对象中保存并在 FragmentActivity 的 onRetainNonConfigurationInstance() 方法中返回即可:
/**
Called by the system, as part of destroying an
activity due to a configuration change, when it is known that a new
instance will immediately be created for the new configuration. You
can return any object you like here, including the activity instance
itself, which can later be retrieved by calling
{@link #getLastNonConfigurationInstance()} in the new activity
instance.
*/
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
// …省略与原理无关代码
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
这样,在顶层源头上就保证了所有 Controller 的 ViewModels 不会在发送配置变化的时候由于 Controller 重建而被销毁。
另外在 Fragment 层中,必须区分 Fragment 实例销毁时到底是因为调用了 onDestroy 还是配置发生了变化,如果是前者则必须清理自身持有的 ViewModelStore,如果是后者则不能清理:
如图所示,也说明了 Fragment 的 ViewModel 生命周期与该 Fragment 生命周期是一致的。