系统源码参考自 Android API 30 Platform,以及 androidx.lifecycle:lifecycle-viewmodel:2.2.0
在文章开始之前,有如下几点疑惑,先记录下来:
ViewModel#onCleared()
的调用时机 ?- ViewModel 与
onSaveInstanceState()
有联系吗?或者有什么区别吗? - ViewModel 是否涉及序列化与反序列化?
- 一个 Activity 中是否支持多个 ViewModel ?
ViewModelProvider#get()
的使用时机。
1. 与 ViewModel
相关的类
1.1 ViewModelStore
和 ViewModelStoreOwner
ViewModel
实现的关键就是 ViewModelStore
,其内部有一个 HashMap<String, ViewModel>
用于存储多个 ViewModel
实例,即本质上就是一个用来维护多个 ViewModel
实例的 map
容器。
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
...
}
另外,还涉及到 ViewModelStoreOwner
接口。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
1.2 androidx.activity.ComponentActivity
androidx.activity.ComponentActivity
(源码基于 androidx.activity:activity:1.1.0
),就是我们常用的 androidx.appcompat.app.AppCompatActivity
的父类。
androidx.activity.ComponentActivity
继承自 androidx.core.app.ComponentActivity
,且实现了 ViewModelStoreOwner
接口,同时内部维护了对应的成员变量 mViewModelStore
(ViewModelStore
类型的)。
// androidx.activity.ComponentActivity
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
// NonConfigurationInstances 为 ComponentActivity 中的内部静态类
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
// 如果前面都没有获取到 ViewModelStore 实例,则这里直接实例化一个新的
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
而 getLastNonConfigurationInstance()
是 android.app.Activity
的成员方法。
// android.app.Activity
/**
* Retrieve the non-configuration instance data that was previously
* returned by {@link #onRetainNonConfigurationInstance()}. This will
* be available from the initial {@link #onCreate} and
* {@link #onStart} calls to the new instance, allowing you to extract
* any useful dynamic state from the previous instance.
*
* <p>Note that the data you retrieve here should <em>only</em> be used
* as an optimization for handling configuration changes. You should always
* be able to handle getting a null pointer back, and an activity must
* still be able to restore itself to its previous state (through the
* normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
* function returns null.
*
* <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
* {@link Fragment#setRetainInstance(boolean)} instead; this is also
* available on older platforms through the Android support libraries.
*
* @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
*/
@Nullable
public Object getLastNonConfigurationInstance() {
// mLastNonConfigurationInstances 则是在 Activity 中定义的内部静态类 ComponentActivity.NonConfigurationInstances 类型
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
在 android.app.Activity
内部维护着成员变量 mLastNonConfigurationInstances
,其是在 Activity#attach()
中被赋值的,其中 lastNonConfigurationInstances
则是从前一个被销毁的 Activity 传递过来的(具体就在第 2 部分展开讲)。
而 getLastNonConfigurationInstance()
的作用就是为了从先前的 Activity 实例获取维护的动态的状态,即通过 mLastNonConfigurationInstances
实现,且在初始的 onCreate()
、onStart()
中可用(会在Activity#performResume()
中被置为 null)。另外,与 getLastNonConfigurationInstance()
对应的是 onRetainNonConfigurationInstance()
(后面会说到)。
// android.app.Activity
final void attach(..., NonConfigurationInstances lastNonConfigurationInstances, ...) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
从上述一连串逻辑可以知道,androidx.activity.ComponentActivity#mViewModelStore
本质上对应着 android.app.Activity#mLastNonConfigurationInstances.activity.viewModelStore
。
2. 在 Activity 的实现原理
接下来要讲的是 ViewModel 在 Activity 的原理,其实也对应着 android.app.Activity#mLastNonConfigurationInstances.activity
在 Activity 销毁重建之间的传递逻辑。
当系统配置发生变更(如切换语言等)、横竖屏切换等导致 Activity 销毁重建时,此时会有如下调用逻辑:
2.1 ActivityThread#handleRelaunchActivityInner()
上述流程是在应用进程中进行的,主要分为两个部分,原有 Activity 的销毁,以及新 Activity 的重建,相关逻辑集中在 ActivityThread#handleRelaunchActivityInner()
中,
// ActivityThread
/**
* @param r 需要被销毁的 activity 对应的 ActivityClientRecord 实例
*/
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
...
// 第四个参数即为最终在 performDestroyActivity() 中用到的 getNonConfigInstance,且为 true
// 变量 r 最终传递到了 performDestroyActivity() 中,并且 r.lastNonConfigurationInstances 被赋值了
handleDestroyActivity(r.token, false, configChanges, true, reason);
...
// destory 上一个 activity 之后,然后 launch 目标 activity,
// 从而将之前 Activity 的 retainNonConfigurationInstances() 传递到目标 Activity
handleLaunchActivity(r, pendingActions, customIntent);
}
2.2 ActivityThread#handleDestroyActivity()
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
// 进一步调用 ActivityThread#performDestroyActivity(),且 getNonConfigInstance 从 handleRelaunchActivityInner() 传入,且恒为 true
ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason);
...
}
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
// 根据 token 获取到在应用进程维护的对应的 ActivityClientRecord 实例 r,且与需要被销毁的 activity 存在关联,即 r.activity
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
// Calls Activity#onPause(), "destroy" is a param for "reason"
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
// Calls Activity#onStop() and Activity#onSaveInstanceState(Bundle), and updates the client record's state.
callActivityOnStop(r, false /* saveState */, "destroy");
}
// 在上游调用方(handleRelaunchActivityInner())中被设置为 true
if (getNonConfigInstance) {
try {
// 调用需要被销毁的 activity.retainNonConfigurationInstances(),从而获取到需要被保留的 ViewModelStore,
// 且先临时赋值给 r.lastNonConfigurationInstances
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
...
}
}
try {
r.activity.mCalled = false;
// Calls Activity#onDestory()
mInstrumentation.callActivityOnDestroy(r.activity);
...
} catch (SuperNotCalledException e) {
...
}
r.setState(ON_DESTROY);
}
...
return r;
}
需要注意的是,performDestroyActivity()
并不是单纯的处理 Activity#onDestory()
的,而是为了 destory activity 处理一系列逻辑。
其中,需要关注的是 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances()
,r.activity
即需要被销毁的 Activity
实例。
// android.app.Activity
NonConfigurationInstances retainNonConfigurationInstances() {
// onRetainNonConfigurationInstance() 会在 androidx.activity.ComponentActivity 被重写
Object activity = onRetainNonConfigurationInstance();
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
...
return nci;
}
// androidx.activity.ComponentActivity
/**
* 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
public final Object onRetainNonConfigurationInstance() {
// mViewModelStore 会在 getViewModelStore() 的时候被赋值或者初始化
ViewModelStore viewModelStore = mViewModelStore;
// 如果 viewModelStore 为 null,则表示 getViewModelStore() 从没有被调用过,即上层使用者在当前需要被销毁的 Activity 中
// 没有实例化过 ViewModelProvider,即进一步表明没有创建过 ViewModel。
// 但是反过来,没有创建过 ViewModel,getViewModelStore() 也有可能会被调用(具体见第 4 节问题(5))
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
// 尝试经由 getLastNonConfigurationInstance() 看是否之前有 viewModelStore
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
// 走到此处时,viewModelStore 如果还不为 null,则表示之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity) 有维护着一个 viewModelStore 实例,
// 因此在当前 Activity 被销毁的时候,还需要将其传承下去(即传递到下一个将被重建的 Activity)
NonConfigurationInstances nci = new NonConfigurationInstances();
...
nci.viewModelStore = viewModelStore;
return nci;
}
在 androidx.activity.ComponentActivity#onRetainNonConfigurationInstance()
中,主要是为了得到当前需要被销毁的 Activity 维护的 mViewModelStore
,如果为 null,则进一步通过父类方法 android.app.Activity#getLastNonConfigurationInstance()
去获取 android.app.Activity#mLastNonConfigurationInstances.activity
。
而 android.app.Activity#mLastNonConfigurationInstances
在第 1 部分有讲,是从上一次被销毁的 Activity 传递过来的,而 mLastNonConfigurationInstances.activity
即对应着上一次被销毁的 Activity 维护的 mViewModelStore
。
因此,androidx.activity.ComponentActivity#onRetainNonConfigurationInstance()
关于 ViewModel 实现的意义,简单的说就是要拿到之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity)维护的且一直传递着的 viewModelStore 实例,使其接着继续传递到下一个被重建的 Activity 中。
然后回到 performDestroyActivity()
的
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances()
该处逻辑的作用就是得到 android.app.Activity.NonConfigurationInstances
实例对象(而该对象会间接(NonConfigurationInstances#activity.viewModelStore
)持有着之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity)维护的一直传递着的 viewModelStore 实例),并将其赋值到给 r.lastNonConfigurationInstances
。
小节
ActivityThread#handleDestroyActivity()
的作用就是为了处理要被销毁的 Activity 的,以及在销毁之前,获取到之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity)维护的一直传递着的 viewModelStore 实例。
2.2 ActivityThread#handleLaunchActivity()
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
...
// 进一步调用 performLaunchActivity(),并把 r 传递进去
final Activity a = performLaunchActivity(r, customIntent);
...
return a;
}
可以看到,handleLaunchActivity()
会进一步调用 performLaunchActivity()
,关键的就是参数 r
,其是最开始是从 handleRelaunchActivityInner()
中传递过来的,且在 handleLaunchActivity()
之前,r
被传入 performDestroyActivity()
中,实现了 r.lastNonConfigurationInstances
持有要被传递到重建 Activity 中的 viewModelStore 实例。
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
// 创建新的 Activity 实例
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
...
if (activity != null) {
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
if (customIntent != null) {
activity.mIntent = customIntent;
}
// 清除 old activity 对应的 r.lastNonConfigurationInstances 的引用
r.lastNonConfigurationInstances = null;
...
} catch (Exception e) {
...
}
return activity;
}
接着是 performLaunchActivity()
,内部处理关于重建 Activity 的逻辑,如创建新的 Activity 实例等,其中,关键的是,会调用新的 Activity 实例的 attach()
,并传入 r.lastNonConfigurationInstances
,从而实现将之前的 viewModelStore 实例传入到新的 Activity 之中(与之前说的 Activity#attach()
相呼应)。
小节
ActivityThread#handleLaunchActivity()
的作用则是处理新 Activity 的重建流程,并把之前维护的 viewModelStore 实例传递到新 Activity 中。
至此, ViewModel 在 Activity 中的实现的关键就是这些了。这一系列的流程,都是在应用进程中处理的,这其实就能说明 ViewModel 的实现的本质,其实就是对象在内存中保留与传递。
3. ViewModel#onCleared()
调用时机
3.1 Old ViewModel 被覆盖时
在使用 ViewModelProvider#get()
获取 ViewModel 实例的时候,最终都会走到如下方法:
// ViewModelProvider.java
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
// tag 1
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
其中,方法参数 key,其取值会有两种,第一种是由用户自定义的,即直接调用 ViewModelProvider#get(key, modelClass)
,此时 key
完全由用户自己控制。还有一种是调用 ViewModelProvider#get(modelClass)
,此时 key
为默认的,是与 modelClass
的 class name 相关的关键字。
private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
默认的 key = DEFAULT_KEY + ":" + modelClass.getCanonicalName()
因此对于用户自定义 key 的情况,此时就需要通过 tag 1 处的逻辑,来判断重复调用 ViewModelProvider#get(key, modelClass2)
且 key 与之前的值重复时,通过重复的 key 获取到的 ViewModel1 实例类型是否与 modelClass2 对应,如果不对应的话,则表示要基于 modelClass2 来生成一个新的 ViewModel2 实例保存起来,且把老的 ViewModel1 在 mViewModelStore 中覆盖掉(通过调用 mViewModelStore.put()
),然后回调老的 ViewModel1.onCleared()
。
// ViewModelStore.java
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
需要注意,Class#isInstance()
的作用与 instanceof(Java) 是等价的,只不过 instanceof 是关键字,isInstance()
是 Class 的成员方法。因此对于通过相同的 key 来获取 ViewModel 实例时,如果前(子类)后(父类)的 modelClass 存在继承关系时,则会后面的父类 ViewModel 不会被实例化。
open class FatherViewModel: ViewModel()
class SubViewModel: FatherViewModel()
// 同时调用下面两行的逻辑,都使用相同的 key
ViewModelProvider(owner).get("test_key", SubViewModel::class.java) // line 1
ViewModelProvider(owner).get("test_key", FatherViewModel::class.java) // line 2
在 line1 会先生成 SubViewModel 实例对象 model1 并存到 mViewModelStore
中,然后在 line 2 的时候,因为都是用的 test_key
,因此从 mViewModelStore 中获取到 model1 之后,再判断 FatherViewModel::class.java.isInstance(model1)
,此时条件成立,则会直接返回 model1。
3.1 androidx.activity.ComponentActivity
正常 destroy 时
在 androidx.activity.ComponentActivity
的无参构造方法会通过 lifecycle 回调监听 ON_DESTROY
事件,在判断到 Activity 是正常被销毁时,而不是因为配置发生改变去销毁 Activity 而 ON_DESTROY
时,会正常调用 ViewModelStore#clear()
。
// androidx.activity.ComponentActivity.java
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
...
}
// ViewModelStore.java
public final void clear() {
for (ViewModel vm : mMap.values()) {
// ViewModel#clear() 内部会调用 ViewModel#onCleared()
vm.clear();
}
mMap.clear();
}
4. 问题的解答
在文章最前面留了几个小问题,这里统一的处理下。
(1)ViewModel#onCleared()
的调用时机 ?
这个参考上文的第 3 节即可。
(2)ViewModel 与 onSaveInstanceState()
有联系吗?或者有什么区别吗?
可以参考 getLastNonConfigurationInstance() 的注释:
Note that the data you retrieve here should only be used as an optimization for handling
configuration changes. You should always be able to handle getting a null pointer back,
and an activity must still be able to restore itself to its previous state (through the
normal onSaveInstanceState(Bundle) mechanism) even if this function returns null.
两者的相同之处,都是为了保留数据。但是使用场景不一样:
- ViewModel 是为了在系统配置改变导致 Activity 销毁重建时维护保留数据用的,数据是维护在应用进程内存中的,
即动态的;
- onSaveInstanceState(outState: Bundle) 则是为了应对 Activity 被切换到后台时可能会被系统回收的情况,
涉及到应用进程的销毁重建,会涉及跨进程通信。
因此对于 ViewModel 中维护的数据,也需要在 onSaveInstanceState() 时把必要的数据维护到 outState 中,避免在
切换到后台时被系统回收而导致应用进程重建时数据的丢失。
(3)ViewModel 是否涉及序列化与反序列化?
显然,是不涉及的,数据不涉及跨进程通信,都是直接在当前应用进程的内存中传递。
(4)一个 Activity 中是否支持多个 ViewModel ?
准确的说,是支持多个 key 不一致的 ViewModel 对象,因为 ViewModelStore 的本质是一个维护 Key-ViewModel 的
map 容器。
(5)ViewModelProvider#get()
的使用时机。
显而易见,最早的调用时机是要在 Activity#attach() 之后,这个在
androidx.activity.ComponentActivity#getViewModelStore() 中也有提到:
Your activity is not yet attached to the Application instance.
You can't request ViewModel before onCreate call.
那是不是一定要在 onCreate()、onStart() 中调用呢?在 onResume() 中是否可以呢?毕竟在 1.2 小节提到了
android.app.Activity.getLastNonConfigurationInstance() 与 mLastNonConfigurationInstances,
以及 mLastNonConfigurationInstances 的有效周期是在初始的 onCreate()、onStart(),之后会在
Activity#performResume() 中被置为 null。
先说答案,ViewModelProvider#get() 在 onResume() 也能够正常使用。
这是因为 androidx.activity.ComponentActivity#getViewModelStore() 在 onCreate() 之前就会被
系统调用到(具体调用时机有待探究,但是可以通过重写 getViewModelStore() 来证明 ),因此对于借助
mLastNonConfigurationInstances 的 ViewModelSrtore,并不会受到其会在 Activity#performResume()
中被置为 null 的影响。