Configuration changed事件传递流程
当设备配置发生变更时,系统会调用AMS的updateConfiguration()方法,来通知AMS处理configuration changed事件,ActivityManagerService#updateConfiguration()源码如下:
public boolean updateConfiguration(Configuration values) {
// ... 省略一段代码
synchronized(this) {
// ... 省略一段代码
try {
if (values != null) {
Settings.System.clearConfiguration(values);
}
updateConfigurationLocked(values, null, false, false /* persistent */,
UserHandle.USER_NULL, false /* deferResume */,
mTmpUpdateConfigurationResult);
return mTmpUpdateConfigurationResult.changes != 0;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
可以看到ActivityManagerService#updateConfiguration()内部调用了ActivityManagerService#updateConfigurationLocked(),该方法一共做了两件事:
- 调用
ActivityManagerService#updateGlobalConfigurationLocked()更新当前配置信息 - 调用
ActivityManagerService#ensureConfigAndVisibilityAfterUpdate()确保给定的activity使用的是当前配置。如果返回true表示activity未被重启,否则让该activity destroyed以适配当前配置。
ActivityManagerService#ensureConfigAndVisibilityAfterUpdate()的源码如下:
private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
// 获取当前拥有焦点的activity
final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
starting = mainStack.topRunningActivityLocked();
}
if (starting != null) {
// 关键代码
kept = starting.ensureActivityConfiguration(changes,
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
!PRESERVE_WINDOWS);
}
}
return kept;
}
这里可以看到,真正完成Activity配置更新的是ActivityRecord#ensureActivityConfiguration()方法,该方法内部调用了它的重载方法,其源码如下:
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreStopState) {
final ActivityStack stack = getStack();
// 如果马上就会再次调用updateConfiguration(),则忽略本次修改,交由下次处理,节省时间
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + this);
return true;
}
// We don't worry about activities that are finishing.
// 如果当前activity已经finish则忽略
if (finishing) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration doesn't matter in finishing " + this);
stopFreezingScreenLocked(false);
return true;
}
// ...省略一段代码
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
// yet. We do, however, return after applying the config to activity record, so that
// it will use it for launch transaction.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check for initializing activity: " + this);
return true;
}
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
startFreezingScreenLocked(app, globalChanges);
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
if (app == null || app.thread == null) {
// ...省略log代码
// 如果app不在托管状态,则仅销毁当前activity
stack.destroyActivityLocked(this, true, "config");
} else if (mState == PAUSING) {
// ...省略log代码
// 如果当前activity处于PAUSING状态,则标记其需要重启,等到PAUSING后reLaunch
deferRelaunchUntilPaused = true;
preserveWindowOnDeferredRelaunch = preserveWindow;
return true;
} else if (mState == RESUMED) {
// ...省略一段代码
// 如果当前activity处于RESUMED状态,则重启后需要恢复到RESUMED状态
relaunchActivityLocked(true /* andResume */, preserveWindow);
} else {
// ...省略log代码
relaunchActivityLocked(false /* andResume */, preserveWindow);
}
// activity自行处理了configuration changed,无需重启
return false;
}
// Activity可以自己处理配置变更则走这里
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
scheduleConfigurationChanged(newMergedOverrideConfig);
}
return true;
}
可以看到决定是否需要reLaunch的关键代码是ActivityRecord#shouldRelaunchLocked(changes, mTmpConfig)。另外一个值得关注的点是forceNewConfig变量,如果改值为true,则会忽略activity的configChanges配置,强制重启activity。其值仅在调用ActivityStack#restartPackage()时为true。ActivityRecord#shouldRelaunchLocked的源码如下:
private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
// 获取manifest中配置的configChanges属性
int configChanged = info.getRealConfigChanged();
boolean onlyVrUiModeChanged = onlyVrUiModeChanged(changes, changesConfig);
// Override for apps targeting pre-O sdks
// If a device is in VR mode, and we're transitioning into VR ui mode, add ignore ui mode
// to the config change.
// For O and later, apps will be required to add configChanges="uimode" to their manifest.
if (appInfo.targetSdkVersion < O
&& requestedVrComponent != null
&& onlyVrUiModeChanged) {
configChanged |= CONFIG_UI_MODE;
}
// 关键代码
return (changes&(~configChanged)) != 0;
}
没错,正是(changes&(~configChanged)) != 0通过比对变更事件是否在Activity自己可以处理的范围内来决定是否reLaunch,而configChanged正是我们在Manifest.xml中配置的android:configChanges属性。
reLunch的调用流程
我们接着往下看,可以发现真正执行reLuanch逻辑的是ActivityRecord#relaunchActivityLocked()方法:
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
// ...省略一段代码
try {
// ...省略log代码
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(service.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
// 关键代码1
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
// 关键代码2
service.getLifecycleManager().scheduleTransaction(transaction);
} catch (RemoteException e) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
}
// ...省略一段代码
}
首先我们看关键代码2。
service.getLifecycleManager()返回的是一个ClientLifecycleManager的实例,其scheduleTransaction(ClientTransaction)方法最终会调用ClientTransaction#schedule():
public void schedule() throws RemoteException {
// mClient为ApplicationThead类型变量
mClient.scheduleTransaction(this);
}
最终消息通过ApplicationThead转发到了ActivityThread,并调用ActivityThread#sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction)交由H处理。
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
break;
H会通过TransactionExecutor执行该ransaction,内部会遍历通过addCallback()传递给transaction的callbacks,callbacks的execute(ClientTransactionHandler, IBinder,PendingTransactionActions)都会被调用,而此时callback接收到的ClientTransactionHandler对象是类型正是ActivityThread。
让我们回到关键代码1.这里添加了一个ActivityRelaunchItem类型的callback。它继承自ClientTransactionItem,在它execute()中调用了ClientTransactionHandler#handleRelaunchActivity(mActivityClientRecord, pendingActions)来执行reluanch真正逻辑:
public void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {
if (mActivityClientRecord == null) {
if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
return;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
通过对关键代码2的分析,我们知道这里实际会调用ActivityThread#handleRelaunchActivity():
public void handleRelaunchActivity(ActivityClientRecord tmp,PendingTransactionActions pendingActions) {
// ...省略一段代码
if (changedConfig != null) {
mCurDefaultDisplayDpi = changedConfig.densityDpi;
updateDefaultDensity();
handleConfigurationChanged(changedConfig, null);
}
ActivityClientRecord r = mActivities.get(tmp.token);
// ...省略一段代码
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
// ...省略一段代码
}
在handleRelaunchActivityInner()中,先调用ActivityThread#handleDestroyActivity()销毁当前activity,随便调用ActivityThread#handleLaunchActivity()重启了activity。我们都知道,Activity有一个回调方法onRetainNonConfigurationInstance(),当设备信息变更时,会保存该方法返回的Object,之后可以在重启的Activity中通过getLastNonConfigurationInstance()获取该Object。onRetainNonConfigurationInstance()并非仅会在发生reLaunchActivity时回调,而是在Activity destoryed时,在ActivityThread.performDestroyActivity()中调用Activity.retainNonConfigurationInstances()获取的。该方法返回的是NonConfigurationInstances,其activity属性便是调用Activity.onRetainNonConfigurationInstance()获取的。而之所以getLastNonConfigurationInstance()能获取到值,是因为在reLaunchActivity中将同一ActivityRecord作为参数,传递给了新Activity。该方法是在ComponentActivity,已经被重写为final方法,子类如果想保存数据,可以通过onRetainCustomNonConfigurationInstance替代,但是官方推荐使用ViewModel组件来替代它,而ViewModel之所以会在设备旋转后恢复,便是通过这种方式保存的。
Activity#onConfigurationChanged(Configuration)调用时机
现在要解决的疑惑是,当变更的配置在activity自处理的配置列表时,activity仅会回调onConfigurationChanged(Configuration),这又是在哪里调用的呢?答案就在ActivityRecord的ensureActivityConfiguration()方法中。
// Activity可以自己处理配置变更则走这里
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
scheduleConfigurationChanged(newMergedOverrideConfig);
}
这两个分支,最终都会调用ClientTransactionHandler.handleActivityConfigurationChanged()方法,该方法由ActivityThread实现:
public void handleActivityConfigurationChanged(IBinder activityToken,Configuration overrideConfig, int displayId) {
// ...省略一段代码
final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
&& displayId != r.activity.getDisplay().getDisplayId();
// ...省略一段代码
if (movedToDifferentDisplay) {
// ...省略一段代码
final Configuration reportedConfig = performConfigurationChangedForActivity(r,
mCompatConfiguration, displayId, true /* movedToDifferentDisplay */);
// ...省略一段代码
} else {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
+ r.activityInfo.name + ", config=" + overrideConfig);
performConfigurationChangedForActivity(r, mCompatConfiguration);
}
// ...省略一段代码
}
performConfigurationChangedForActivity()最终会调用performActivityConfigurationChanged()方法,该方法如下:
private Configuration performActivityConfigurationChanged(Activity activity,Configuration newConfig, Configuration amOverrideConfig, int displayId,boolean movedToDifferentDisplay) {
// ...省略一段代码
boolean shouldChangeConfig = false;
if (activity.mCurrentConfig == null) {
shouldChangeConfig = true;
} else {
// If the new config is the same as the config this Activity is already running with and
// the override config also didn't change, then don't bother calling
// onConfigurationChanged.
final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
amOverrideConfig)) {
// Always send the task-level config changes. For system-level configuration, if
// this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged as we're going to destroy it.
// 如果共用配置发生变更
// mUpdatingSystemConfig这里为false所以shouldChangeConfig=true
if (!mUpdatingSystemConfig
|| (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
|| !REPORT_TO_ACTIVITY) {
shouldChangeConfig = true;
}
}
}
if (!shouldChangeConfig && !movedToDifferentDisplay) {
// Nothing significant, don't proceed with updating and reporting.
return null;
}
// ...省略一段代码
if (shouldChangeConfig) {
activity.mCalled = false;
activity.onConfigurationChanged(configToReport);
if (!activity.mCalled) {
throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
" did not call through to super.onConfigurationChanged()");
}
}
return configToReport;
}
最终我们找到了activity.onConfigurationChanged(configToReport)调用位置。至此,设备变更是,activity的生命周期调用流程分析完毕。
Fragment#setRetainInstance的实现原理
众所周知,通过调用Fragment#setRetainInstance(true),我们可以在因设配配置发生变更而重建时保留Fragment而不是销毁重建。那么这一效果又是如何实现的呢?
我们先来看看Fragment#setRetainInstance():
public void setRetainInstance(boolean retain) {
mRetainInstance = retain;
if (mFragmentManager != null) {
if (retain) {
mFragmentManager.addRetainedFragment(this);
} else {
mFragmentManager.removeRetainedFragment(this);
}
} else {
mRetainInstanceChangedWhileDetached = true;
}
}
继续追踪发现,Fragment实例被添加到了FragmentManagerViewModel.mRetainedFragments,这是一个HashSet类型的变量,以为这同一仅允许被添加一次。那么这些Fragment是在何时保存的呢?想想之前ViewModel的保存,没错就是Activity#retainNonConfigurationInstances():
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
// 保存 FragmentManagerNonConfig
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
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;
}
我们追踪mFragments.retainNestedNonConfig()看下去,发现调用流程如下:
FragmentController#retainNestedNonConfig() -> FragmentManagerImpl#retainNonConfig() ->FragmentManagerViewModel#getSnapshot(), FragmentManagerViewModel#getSnapshot()方法如下:
FragmentManagerNonConfig getSnapshot() {
if (mRetainedFragments.isEmpty() && mChildNonConfigs.isEmpty()
&& mViewModelStores.isEmpty()) {
return null;
}
HashMap<String, FragmentManagerNonConfig> childNonConfigs = new HashMap<>();
for (Map.Entry<String, FragmentManagerViewModel> entry : mChildNonConfigs.entrySet()) {
FragmentManagerNonConfig childNonConfig = entry.getValue().getSnapshot();
if (childNonConfig != null) {
childNonConfigs.put(entry.getKey(), childNonConfig);
}
}
mHasSavedSnapshot = true;
if (mRetainedFragments.isEmpty() && childNonConfigs.isEmpty()
&& mViewModelStores.isEmpty()) {
return null;
}
return new FragmentManagerNonConfig(
new ArrayList<>(mRetainedFragments),
childNonConfigs,
new HashMap<>(mViewModelStores));
}
可以看到,如果我们设置了保留Fragment,那么被保留的Fragment最终都会保存在FragmentManagerNonConfig变量中,从而被Activity#retainNonConfigurationInstances()所保存,之后作为参数传给reluanch的Activity,至此Fragment保存完毕。
下面我们来看看保留的Fragment是如何被恢复的。
通过阅读Activity#onCreate()方法我们发现,如果Activity.mLastNonConfigurationInstances不为null,即设备配置发生变更保存了相关数据,那么将会对Fragment进行状态恢复。Fragment的状态恢复的关键代码是mFragments.restoreAllState(),这里的mFragments是FragmentController的实例,该方法的内部最终调用了FragmentManagerImpl#restoreSaveState(),我们来看看该方法的实现:
void restoreSaveState(Parcelable state) {
// ...省略一段代码
// First re-attach any non-config instances we are retaining back
// to their saved state, so we don't try to instantiate them again.
// 遍历保留的Fragments
for (Fragment f : mNonConfig.getRetainedFragments()) {
if (DEBUG) Log.v(TAG, "restoreSaveState: re-attaching retained " + f);
FragmentState fs = null;
// 获取之前处于Active状态的Fragments状态
for (FragmentState fragmentState : fms.mActive) {
if (fragmentState.mWho.equals(f.mWho)) {
fs = fragmentState;
break;
}
}
// 未获取到状态直接变更生命周期
if (fs == null) {
if (DEBUG) {
Log.v(TAG, "Discarding retained Fragment " + f
+ " that was not found in the set of active Fragments " + fms.mActive);
}
// We need to ensure that onDestroy and any other clean up is done
// so move the Fragment up to CREATED, then mark it as being removed, then
// destroy it.
moveToState(f, Fragment.CREATED, 0, 0, false);
f.mRemoving = true;
moveToState(f, Fragment.INITIALIZING, 0, 0, false);
continue;
}
// 恢复Fragment的状态
fs.mInstance = f;
f.mSavedViewState = null;
f.mBackStackNesting = 0;
f.mInLayout = false;
f.mAdded = false;
f.mTargetWho = f.mTarget != null ? f.mTarget.mWho : null;
f.mTarget = null;
if (fs.mSavedFragmentState != null) {
fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
f.mSavedFragmentState = fs.mSavedFragmentState;
}
}
// ...省略一段代码
}
至此保留的Fragments在的状态恢复完成。