ViewModel 的实现原理

Android Jetpack 提供了 ViewModel, 以用来维护 Activity 或 Fragment 中需要的数据。其能够在因配置改变(比如屏幕旋转)造成的 Activity 和 Fragment 重建时, 依然维护其内部的数据。

背景

首先,根据官方文档能得出 ViewModel 的适用场景(也可以叫出现的目的):

  • 在发生配置改变时 Activity 和 Fragment 会被销毁重建,它们内部的临时性数据(不是通过 Intent 传入的数据)就会丢失. 如果把这些临时数据放到 ViewModel 中, 则可以避免数据的丢失。当然也可以利用 onSaveInstanceState 来保留临时数据,但是如果临时数据的量较大,onSaveInstanceState 由于涉及了跨进程通信,较大的数据量会造成 marshallingunmashlling 消耗较大。而利用 ViewModel 其实是没有跨进程通信的消耗。但是它没有 onSaveInstanceState 提供的 Activity 被回收之后的数据恢复功能:在 Activity 位于后台时系统会在内存不足时将其回收,当 Activity 再次回到前台时,系统会把 onSaveInstanceState 中保存的数据通过 onRestoreInstanceStateonCreate 里的 savedInstanceState 参数传递给 Activity。
  • ViewModel 顾名思义能知道它本质就是用来储存与视图相关的数据的,官方也是推荐将 Activity(Fragment) 中的数据及数据操作提取到 ViewModel 中,让视图的显示控制和数据分离。感觉就是利用 ViewModel 做 MVP 模式了。
  • ViewModel 能感知 Activity或Fragment 的生命周期的改变,在 Activity或Fragment 销毁时执行一些数据清理工作(ViewModel 的实现类可以通过重写onCleared方法)。

先摊牌

后面的查找实现原理的内容可能较多,可能会让读者觉得较拖沓,因此先抛出经过查找实现原理而找到的结论。

我将 ViewModel 的实现原理总结为:隔山打牛

  • Activity(Fragment) 的 ViewModel 都存储在 ViewModelStore 中,每个 Activity(Fragment) 都会拥有一个 ViewModelStore 实例
  • ViewModelProvider 负责向使用者提供访问某个 ViewModel 的接口,其内部会持有当前 Activity(Fragment) 的 ViewModelStore,然后将操作委托给 ViewModelStore 完成
  • ViewModel 能在 Activity(Fragment) 在由于配置重建时恢复数据的实现原理是:Activity(指 support library 中的 ComponentActivity) 会将 ViewModelStore 在 Activity(Fragment) 重建之前交给 ActivityThread 中的 ActivityClientRecord 持有,待 Activity(Fragment) 重建完成之后,再从 ActivityClientRecord 中获取 ViewModelStore
  • 如果应用的进程位于后台时,由于系统内存不足被销毁了。即使利用 ViewModel 的也不能在 Activity(Fragment) 重建时恢复数据。因为存储 ViewModel 的 ViewModelStore 是交给 ActivityThread 中的 ActivityClientRecord 暂存的,进程被回收了,ActivityThread 也就会被回收,ViewModelStore 也就被回收了,ViewModel 自然不复存在了

分析入口

JetPack 提供的组件都是基于 Android SDK 现有组件进行的封装, 没有修改 SDK 已有的那些类。ViewModel 也是如此, 其实现是基于 AndroidX(以前的 support library)中的 ComponentActivity

结合 ViewModel 的功能特点,我将从以下几个方面入手,进而理清 ViewModel 的实现原理。

  1. ViewModel 中涉及的类和数据结构。
  2. ViewModel 如何保证 Activity 或 Fragment 因为配置改变而发生重建时,ViewModel 中的数据能得以保留。

核心数据结构

核心数据结构将介绍 ViewModel 中涉及的一些类和数据结构。

ViewModel

ViewModel 是一个抽象类,使用者需要继承它,ViewModel 内部的变量和方法较少。

private volatile boolean mCleared = false;

表示当前的 ViewModel 是否已经被销毁了。

protected void onCleared()

子类通过复写这个方法能够在 ViewModel 被销毁时进行额外的操作(比如释放资源等)。

final void clear()

ViewModelStore

ViewModelStore 顾名思义,它是负责储存 ViewModel 的一个类。引用 ViewModelStore 代码注释中的一段话表示它的功能:

ViewModelStore 的实例必须在发生配置更改时得以保留:如果此 ViewModelStore 的所有者由于配置的改变而被销毁并重新创建,那么所有者的新实例应该具有相同的 ViewModelStore 旧实例。

HashMap<String, ViewModel> mMap = new HashMap<>();

mMap 是ViewModelStore 中有且仅有的成员变量,看它的泛型类型参数就能明白,它就是 ViewModelStore 用来存储 ViewModel 的池子。

final void put(String key, ViewModel viewModel)

向 ViewModelStore 的池子中存入 ViewModel, 如果池子中已经有 key 对应 ViewModel了,旧的会被新的替换,而且会调用旧的 ViewModel 的 onCleared 方法。(🤔️不是应该调用 ViewModel 的 clear 去释放资源吗?)

1
2
3
4
5
6
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}

ViewModel get(String key)

从池子(mMap)中获取 key 对应的 ViewModel

1
2
3
final ViewModel get(String key) {
return mMap.get(key);
}

Set<String> keys()

返回 mMap 的所有 key

final void clear()

清空 mMap 中的所有 ViewModel,并调用每一个的 clear 方法

1
2
3
4
5
6
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}

这里注意下 ViewModelStore 的 get 和 put 方法的声明,访问权限都是包层级的,也就表示我们使用者是无法直接通过 ViewModelStore 通过 key 拿到对应的 ViewModel 的。

ViewModelProvider

一个为范围(Application, Activity, Fragment)提供 ViewModels 的实用工具类

先看下 ViewModelProvider 有哪些字段:

private final Factory mFactory;

1
2
3
public interface Factory {
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

Factory 表示创建 ViewModel 的工厂,final 的声明表示它必须在 ViewModelProvider 的构造函数中就赋值。

final ViewModelStore mViewModelStore;

final 的声明表示 mViewModelStore 也必须在构造函数中就赋值。

static final String DEFAULT_KEY ="androidx.lifecycle.ViewModelProvider.DefaultKey";

DEFAULT_KEY,用来在提交 ViewModel 到 ViewModelStore 时构造 key

以下是 ViewModelProvider 中的方法:

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}

public T get(@NonNull Class modelClass)
public T get(@NonNull String key, @NonNull Class modelClass)

以上两个方法就是获取 ViewModel 的方法,也是 ViewModel 库向使用者提供的两个公开的接口。第一个 get 方法会利用 DEFAULT_KEY 构造一个 key,然后调用第二个 get 方法。

1
2
3
4
5
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
// 构建一个 key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 从 ViewModelStore 中获取 ViewModel
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
}
}
// ViewModelStore 中还没有对应 ViewModel, 就创建
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 将创建的 ViewModel 存入 ViewModelStore
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}

ViewModelStoreOwner

ViewModelStoreOwner 是一个接口,它声明了一个 getViewModelStore 方法需要实现类实现。

实现此接口的类的职责是保留其拥有的 ViewModelStore 在配置更改期间不会被销毁。

1
2
3
4
5
public interface ViewModelStoreOwner {

@NonNull
ViewModelStore getViewModelStore();
}

ComponentActivity

ComponentActivity 是 androidx.activity 包中增加的,FragmentActivity 继承自它。

1
2
3
4
5
6
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner

ComponentActivity 中与 ViewModel 有关的两个变量:

ViewModelStore mViewModelStore;

表示当前 Activity 的 ViewModelStore, 一个 Activity 拥有一个 ViewModelStore

ViewModelProvider.Factory mDefaultFactory;

创建 ViewModel 的工厂实现,ComponentActivity 中使用的是 SavedStateViewModelFactory, SavedStateViewModelFactory 与 Activity 的重建相关。

ViewModelStore getViewModelStore();

ComponentActivity 中实现了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public ViewModelStore getViewModelStore() {
// 如果 mViewModelStore 还未赋值,则创建一个实例赋值给它
if (mViewModelStore == null) {
// 与 Activity 重建时恢复数据有关,后面会再详谈
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
// 创建 ViewModelStore, 赋值给 ComponentActivity 的 mViewModelStore 变量
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}

ViewModelStore 的创建

ViewModelStore 是负责储存 ViewModel 的,所以一般是需要先有个容器,才能存 ViewModel。而 ViewModelStore 又是由 ViewModelStoreOwner 提供的。

我通过在 ViewModeStoreOwner 的 getViewModelStore 方法打断点查看方法栈的调用过程。通过几次断点,我发现 FragmentActivity 和非 FragmentActivity 的创建时机是有所区别的,所以后面分开看待。

FragmentActivity

先看 FragmentActivity 的调用栈:

getViewModelStore:262, ComponentActivity (androidx.activity)
getViewModelStore:887, FragmentActivity$HostCallbacks (androidx.fragment.app)
attachController:3022, FragmentManager (androidx.fragment.app)
attachHost:116, FragmentController (androidx.fragment.app)
onCreate:283, FragmentActivity (androidx.fragment.app)
onCreate:18, ViewModelActivity
performCreate:7136, Activity (android.app)
performCreate:7127, Activity (android.app)
callActivityOnCreate:1271, Instrumentation (android.app)
performLaunchActivity:2893, ActivityThread (android.app)
handleLaunchActivity:3048, ActivityThread (android.app)

从调用栈可看出一个 FragmentActivity 会在 onCreate 时调用到 getViewModelStore

至此能得出:只要是继承自 FragmentActivity 的 Activity(比如: AppCompatActivity) 就会在 onCreate 时创建 ViewModelStore 实例,并用自己的 mViewModelStore 变量引用到。

那么为何 FragmentActivity 在 onCreate 时就需要创建 ViewModelStore 呢?

看调用栈能发现 getViewModelStore 方法是在 FragmentManager 的 attachController 方法中调用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
void attachController(@NonNull FragmentHostCallback<?> host, @NonNull FragmentContainer container) {
mHost = host;
// Get the FragmentManagerViewModel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
// 没有嵌套 Fragment 的 Fragment 会走到这里
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
1
2
3
4
5
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
// 创建 FragmentManagerViewModel, viewModelProvider 的 get 内部会把 ViewModel 添加到 ViewModelStore
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore, FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}

原因找到了: FragmentActivity 在 onCreate 里就调用了 getViewModelStore, 是为了让 FragmentManagerViewModel 尽早的添加到 Activity 的 ViewModelStore 中。

其他 Activity

没有继承自 FragmentActivity 的 Activity 在 onCreate 里不会创建 ViewModelStore, 而是等到有用到的地方才会创建。
比如我在自己的 Activity 的 onCreate post 一个 Runnable, 然后按照 ViewModel 文档的描述, 通过 ViewModelProvider 获取 ViewModel:

1
2
3
runOnUiThread {
val mMode = ViewModelProvider(this).get(MMode::class.java)
}

执行之后时的栈为:

getViewModelStore:262, ComponentActivity (androidx.activity)
<init>:94, ViewModelProvider (androidx.lifecycle)
run:20, ViewModelActivity$onCreate$1 
runOnUiThread:6282, Activity (android.app)

ViewModelProvider 与 ViewModel


所以 ViewModelStore 的创建时机在基于 FragmentActivity 的 Activity 和 基于 ComponentActivity 的 Activity 上有所区别。在 FragmentActivity 上是在 onCreate 中创建;在 ComponentActivity 上是在利用 ViewModelProvider 获取 ViewModel 时再创建。

总的来说 ViewModelStore 就是 ComponentActivity 里的一个成员字段,且只会创建一次。

再看 ViewModelProvider,它其实只是一个用来访问 ViewModelStore 的门面,它内部没有存任何数据。所以每次要获取 ViewModel 时都是创建一个新的 ViewModelProvider 实例。

ViewModel 的重建保留

ViewModel 要实现和 onSaveInstanceState 方法同样的功效,那它就需要在 Activity(或Fragment) 销毁时保留内部的数据,待 Activity 重建时恢复数据。

前面提到过,Jetpack 中的库都是基于现有 SDK 实现进行的封装,不会修改 SDK 已有的实现。ViewModel 要实现重建保留的功能,肯定需要一个时机来做保留的动作。我们知道 SDK 已有的实现中,一个 Activity 在因为配置改变而要销毁重建时一定会调用的一个方法就是onSaveInstanceState,所以先去检查下 ComponentActivity 在这个方法中有没有动什么手脚。

1
2
3
4
5
6
7
8
protected void onSaveInstanceState(@NonNull Bundle outState) {
Lifecycle lifecycle = getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
}
super.onSaveInstanceState(outState);
mSavedStateRegistryController.performSave(outState);
}

发现没有与 ViewModel 相关的,于是这条路不通了。无路可走时造路走,最后大法 Find Usage,找出使用到 mViewModelStore 的地方,看看有什么特殊没有。

Field
    mViewModelStore
Found usages  (6 usages found)
    ComponentActivity  (6 usages found)
        onRetainNonConfigurationInstance()  (1 usage found)
            183 ViewModelStore viewModelStore = mViewModelStore;
        getViewModelStore()  (5 usages found)
            266 if (mViewModelStore == null) {
            271 mViewModelStore = nc.viewModelStore;
            273 if (mViewModelStore == null) {
            274 mViewModelStore = new ViewModelStore();
            277 return mViewModelStore;

发现除了 getViewModelStore,另外一个使用的方法是 onRetainNonConfigurationInstance() ,继续查找这个方法的使用方:

Method
    onRetainNonConfigurationInstance()
Found usages  (9 usages found)
    Activity.java  (6 usages found)
        2422 Object activity = onRetainNonConfigurationInstance();
    LocalActivityManager.java  (1 usage found)
        615 Object instance = r.activity.onRetainNonConfigurationInstance();

Activity 的 retainNonConfigurationInstances 方法调用了子类的 onRetainNonConfigurationInstance() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NonConfigurationInstances retainNonConfigurationInstances() {
//需要 Activity 子类实现的 onRetainNonConfigurationInstance 方法
Object activity = onRetainNonConfigurationInstance();

NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}

在查找 Activity 的 retaionNonConfigurationInstances 方法的调用者,Android Studio 显示找不到了。因为它的调用者是 ActivityThread(@hide 注释的),那就进入 ActivityThread 搜索看看。

在 ActivityThread 的 performDestroyActivity 找到了调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
// 从 mActivities 中获取 token 对应的 ActivityClientRecord
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (r != null) {
//...
// 如果参数 getNonConfigInstance 为 true
if (getNonConfigInstance) {
try {
// 将 ViewModelStore 存入 ActivityClientRecord 的 lastNonConfiguratinInstances 字段中
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {}
}
//...
}
mActivities.remove(token);
return r;
}

那么这个 getNonConfigInstance 参数是在哪里赋值为 true 的呢?

getNonConfigInstance写入

通过定位发现:在 ActivityThread.H 收到 RELAUNCH_ACTIVITY 消息时,会走到 performDestroyActivity 方法,并且 在 RELAUNCH_ACTIVITY 消息的消费链上 handleRelaunchActivityInner 方法会把 getNonConfingInstance 赋值为 true

在 Activity destroy 执行完之后,立马会执行 handleLaunchActivity 方法去重建 Activity。

getNonConfigInstance读取

在创建了新的 Activity 对象之后,就会把 lastNonConfigurationInstances 写入到 Activity 的字段中。因为这里的 lastNonConfigurationInstances 一直是存在 ActivityClientRecord (代码中的r)中的,而 ActivityClientRecord 是维护在 ActivityThread 中的,所以 Activity 对象的销毁和重建不会影响 ActivityThread 中的 ActivityClientRecord 对象,也正是因为这样 ActivityClientRecord 才能持有独立与 Activity 生命周期之外的数据。

通过上面知道 lastNonConfigurationInstances 在 ComponentActivity 中存储的实际是 Activity 的 ViewModelStore 对象。所以 ViewModelStore 靠着 ActivityClientRecord 就实现了与 Activity 生命周期的无关性。