ViewModel用来管理Activity或Fragment中的数据, 当Activity配置发生改变时数据并不会丢失。
ViewModel 在Activity 重建时,数据会先保存在ComponentActivity 中的NonConfigurationInstances中,NonConfigurationInstances定义如下
viewModel保存在NonConfigurationInstances的成员变量mMap中 ,mMap是一个HashMap, 以 DEFAULT_KEY+ViewModel的类名 作为Key ,ViewModel作为Value
ViewModel的保存
Activity#retainNonConfigurationInstances方法,用于获取要保存的NonConfigurationInstances
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
return nci;
}
Fragment的保存与恢复也在这里,Fragment的保存与恢复本文就不做深究了 ,只看Viewmodel相关的 ,跟进 onRetainNonConfigurationInstance
onRetainNonConfigurationInstance方法可以看到把封装好的viewModelStore返回了。
总结一下:Activity和ComponentActivity 都有一个的同名的内部类NonConfigurationInstances ,Activity持有了ComponentActivity的NonConfigurationInstances , viewModelStore保存在ComponentActivity的NonConfigurationInstances中 。
那么Activity#retainNonConfigurationInstances方法又是在哪里调用的?
在ActivityThread中搜索retainNonConfigurationInstances
可以看到只有一处搜索结果 ,在performDestroyActivity方法中被调用 。并将数据保存在r.activity中,r为ActivityClientRecord ,从mActivities中获取,mActivities是一个Map,value为ActivityClientRecord 。
那么ViewModel保存的时间又是多久?继续查看performDestroyActivity方法
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
}
}
synchronized (mResourcesManager) {
mActivities.remove(token);
}
return r;
}
}
在performDestroyActivity方法末尾可以看到mActivities又会把当前的ActivityClientRecord移除然后返回,初步可以推断viewmodel保存的时间应该不会很久。 performDestroyActivity方法在ActivityThread中的调用链:
handleRelaunchActivity -> handleRelaunchActivityInner ->handleDestroyActivity -> performDestroyActivity
只要知道ActivityThread# handleRelaunchActivity 在什么地方调用就能知道答案了。
我们知道Activity在配置发生改变后,Viewmodel的数据并不会发生改变。Activity横竖屏切换可以模拟这个场景,我们看下Activity横竖屏切换做了些什么。Activity有个setRequestedOrientation方法,可以切换横竖屏。
public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
if (mParent == null) {
try {
ActivityTaskManager.getService().setRequestedOrientation(
mToken, requestedOrientation);
} catch (RemoteException e) {
// Empty
}
}
}
获取的是ATMS在client 端的Binder代理对象。通过Binder最终会调到ATMS的setRequestedOrientation方法。
ATMS的setRequestedOrientation方法
ActivityRecord和ActivityClientRecord 是一对一的关系, 一个在应用进程一个在服务进程,跟进r.setRequestedOrientation(requestedOrientation);
void setRequestedOrientation(int requestedOrientation) {
setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
task.taskId, requestedOrientation);
}
最终会调用ensureActivityConfiguration。
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreVisibility) {
final ActivityStack stack = getActivityStack();
final int changes = getConfigurationChanges(mTmpConfig);
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
if (!attachedToProcess()) {
} else if (mState == RESUMED) {
relaunchActivityLocked(true /* andResume */, preserveWindow);
} else {
relaunchActivityLocked(false /* andResume */, preserveWindow);
}
return false;
}
return true;
}
又会根据是否要执行resume 来调用relaunchActivityLocked方法,此处情况resume = true。
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
try {
mStackSupervisor.activityRelaunchingLocked(this);
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(mAtmService.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(
getDisplay().mDisplayContent.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
}
}
最终会调用ActivityRelaunchItem #execute , 我们看看ActivityRelaunchItem#execute
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
}
client就是ActivitThread , 接着就调用我们开头的handleRelaunchActivity方法了 。
总结 : ViewModel的数据保存在ActivityClientRecord 中,ActivityClientRecord 又是保存在ActivityThrad 的mActivities中 , 并且ViewModel保存只是暂时的,在Activity重建时从mActivities中移除。
ViewModel的恢复
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
//保存viewmodel的数据到ActivityClientRecord中
handleDestroyActivity(r.token, false, configChanges, true, reason);
//重建Activity
handleLaunchActivity(r, pendingActions, customIntent);
}
在handleDestroyActivity 保存viewmodel数据后 , 接着调用handleLaunchActivity重建activity,省略后的代码如下。
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
final Activity a = performLaunchActivity(r, customIntent);
return a;
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//构建新的Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
//恢复Viewmodel数据
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.lastNonConfigurationInstances = null;
}
r.setState(ON_CREATE);
synchronized (mResourcesManager) {
mActivities.put(r.token, r);
}
}
return activity;
}
Activity.attach方法中将刚刚保存在ActivityClientRecord的NonConfigurationInstances恢复,viewmodel存在NonConfigurationInstances中也就恢复了。