续接上一篇 ViewModel 的文章,分析 ViewModel 在配置改变后是怎么留存数据的,结合 AMS 在页面 Activity 重启时的流程进行深入解析。
在 JetPack ViewModel 使用及原理解析中我们知道,页面在旋转屏幕后会导致配置信息的改变会触发页面的销毁重建,此时 ActivityThread # performDestroyActivity() 方法中会调用 Activity # retainNonConfigurationInstances() 方法,其方法内部调用 onRetainNonConfigurationInstance() 方法来获取页面的状态信息,这时存储有 ViewModel 的 ViewModelStore 会保存在 ActivityClientRecord.lastNonConfigurationInstances,并保存在 Activity 中 NonConfigurationInstances 这个静态内部类中。在页面重建时 ActivityThread # performLaunchActivity() 方法中调用 Activity # attach() 方法,再将 NonConfigurationInstances 实例传给重建后的 Activity 的 mLastNonConfigurationInstances,这样就能做到页面数据留存和恢复。具体流程可查看 Jetpack ViewModel 使用及原理解析 这篇文章。
当 Activity 因配置改变需要销毁重建时,经过层层调用,会调用 ActivityThread # handleRelaunchActivity() 方法。
1. ActivityThread # handleRelaunchActivity() 方法
public final class ActivityThread extends ClientTransactionHandler {
@UnsupportedAppUsage
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
......
// mActivities 是一个 ArrayMap,key 是 IBinder 类型的 token,value 是 ActivityClientRecord
ActivityClientRecord r = mActivities.get(tmp.token);
......
// 分析1 -- 下面解析
// 页面因配置改变需要销毁重建时,mChangingConfigurations 被标记为 true,这个标记位很有用
// 此时当页面的生命周期走到 ON_DESTROY 时,ViewModelStore 也不会做清理操作
r.activity.mChangingConfigurations = true;
......
// 内部继续调用 handleRelaunchActivityInner,注意入参:ActivityClientRecord:r
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
......
}
}
Activity 因配置改变需要重建,在 handleRelaunchActivity() 方法中,会将 activity.mChangingConfigurations 标志位设置为 true,上篇文章有讲到当页面的生命周期走到 Lifecycle.Event.ON_DESTROY 时,Lifecycle 订阅的观察者中将会做清理操作,但是由于 activity.mChangingConfigurations 在这里被置为 true,所以 ViewModelStore 不会做清理操作。
注意这里: ActivityThread # handleRelaunchActivityInner() 方法的入参 ActivityClientRecord,是 ActivityThread # handleRelaunchActivity() 方法中,根据 token 从 mActivities 这个 ArrayMap 中获取的。
2. ActivityThread # handleRelaunchActivityInner() 方法
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
......
// 分析1 -- handleDestroyActivity
// 注意 getNonConfigInstance 传参为 true
handleDestroyActivity(r.token, false, configChanges, true, reason);
// ActivityClientRecord 记录的状态重置,但 lastNonConfigurationInstances 没有置为空
r.activity = null;
......
r.startsNotResumed = startsNotResumed;
r.overrideConfig = overrideConfig;
// 分析2 -- handleLaunchActivity,注意这里的入参 r
handleLaunchActivity(r, pendingActions, customIntent);
}
这里从 分析1 处开始探索源码
3. ActivityThread # handleDestroyActivity() 方法
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
// 继续走到 performDestroyActivity,getNonConfigInstance 值为 true
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
......
}
4. ActivityThread # performDestroyActivity() 方法
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (r != null) {
......
// 入参 getNonConfigInstance 为 true,所以进入条件判断里面
if (getNonConfigInstance) {
try {
// ActivityClientRecord 的 lastNonConfigurationInstances 赋值为
// activity.retainNonConfigurationInstances() 的返回值
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
}
......
}
try {
r.activity.mCalled = false;
// 调用 Activity 的 onDestroy 方法
mInstrumentation.callActivityOnDestroy(r.activity);
}
......
}
......
synchronized (mResourcesManager) {
mActivities.remove(token);
}
......
return r;
}
这里承接 分析2 开始探索源码,注意这里入参传入的 ActivityClientRecord,就是 ActivityThread # handleRelaunchActivityInner() 方法的入参 ActivityClientRecord,是根据 token 从 mActivities 这个 ArrayMap 中获取的,跟上面 ActivityThread # performDestroyActivity() 方法中,通过 mActivities.get(token) 获取到的 ActivityClientRecord 是同一个,它们使用的是同一个 token 从 mActivities 中获取的,即它们操作的是同一个 ActivityClientRecord。
5. ActivityThread # handleLaunchActivity() 方法
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
......
// 继续调用 performLaunchActivity
final Activity a = performLaunchActivity(r, customIntent);
......
return a;
}
6. ActivityThread # performLaunchActivity() 方法
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
.......
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
}
......
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
......
if (activity != null) {
......
appContext.setOuterContext(activity);
// 经过上面的分析,attach 方法中传入的 r.lastNonConfigurationInstances 就是
// ActivityThread # performDestroyActivity() 方法中赋值的
// activity.retainNonConfigurationInstances() 方法的返回值
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);
......
}
r.setState(ON_CREATE);
......
}
......
return activity;
}
7. Activity # attach() 方法
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
@UnsupportedAppUsage
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
......
mWindow = new PhoneWindow(this, window, activityConfigCallback);
......
mMainThread = aThread;
......
mActivityInfo = info;
......
// 将因配置改变销毁 Activity 时留存在 ActivityClientRecord 中持有的
// lastNonConfigurationInstances 赋值给新建的 Activity
mLastNonConfigurationInstances = lastNonConfigurationInstances;
......
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
......
}
public boolean isChangingConfigurations() {
// 如果是因为配置改变需要销毁重建时,mChangingConfigurations 值会被置为 true
return mChangingConfigurations;
}
}
Activity 在 attach() 方法中绑定数据时,将因配置更改销毁 Activity 时留存在 ActivityClientRecord 中持有的 lastNonConfigurationInstances 赋值给新建的 Activity。之后就可以通调用 Activity 的 getLastNonConfigurationInstance() 方法获取到 onRetainNonConfigurationInstance() 方法保留的对象。
8. ComponentActivity 类
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware, LifecycleOwner, ViewModelStoreOwner,
HasDefaultViewModelProviderFactory, ... {
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) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
// 如果是因为配置改变需要销毁重建时,isChangingConfigurations() 返回值为 true 则不清理
// 源码可以看上面第 7 处的注释
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// 感知生命周期变化、获取、创建关联的 ViewModelStore
ensureViewModelStore();
getLifecycle().removeObserver(this);
}
});
}
}
通过 分析1 处的源码,我们可知如果页面 Activity 是因为配置改变需要销毁重建时,Activity 的内部成员变量 mChangingConfigurations 值会被置为 true。
当我们页面的生命周期方法走到 onDestroy() 时,Lifecycle 所绑定的观察者在感知到 Lifecycle.Event.ON_DESTROY 事件时,会触发页面的一些清理操作,其中包括 ViewModelStore 的清理工作。但是如果是因为配置改变导致的销毁重建,此时 Activity # isChangingConfigurations() 方法内部返回的 mChangingConfigurations 是 true,所以 ViewModelStore 并不会清理,其内部存储的 ViewModel 也就得以保留下。
总结: 通过上面一系列源码流程的分析,再结合上一篇文章,对于 ViewModel 在配置改变后是怎么留存数据的,应该有了更加深刻的理解啦!