基于Android U,只保留了关键调用步骤,略去了细节。
当设备配置发生变更时,系统会调用 ATMS 的 updateConfiguration() 方法,来通知 ATMS 处理 configuration change 事件。时序图如下。

java
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public boolean updateConfiguration(Configuration values) {
...
synchronized (mGlobalLock) {
...
try {
...
updateConfigurationLocked(values, null, false, false /* persistent */,
UserHandle.USER_NULL, false /* deferResume */,
mTmpUpdateConfigurationResult); // 1
return mTmpUpdateConfigurationResult.changes != 0;
} finally {
...
}
}
}
注释1处调用 updateConfigurationLocked()。
java
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
boolean initLocale, boolean persistent, int userId, boolean deferResume,
ActivityTaskManagerService.UpdateConfigurationResult result) {
...
try {
if (values != null) {
changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId); // 1
}
if (!deferResume) {
kept = ensureConfigAndVisibilityAfterUpdate(starting, changes); // 2
}
} finally {
...
}
...
return kept;
}
注释1处更新当前配置;
注释2处确保给定的 Activity 使用的是当前配置,如果返回 true 表示 Activity 未被重启,否则让该 Activity destroyed 以适配当前配置。
java
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
...
if (mainRootTask != null) {
...
if (starting != null) {
kept = starting.ensureActivityConfiguration(changes,
false /* preserveWindow */); // 1
...
}
}
return kept;
}
注释1处调用 ActivityRecord#ensureActivityConfiguration() 真正完成 Activity 配置更新。
java
frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
false /* ignoreVisibility */, false /* isRequestedOrientationChanged */);
}
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreVisibility, boolean isRequestedOrientationChanged) {
// 这里省略了一些return ture的分支
// 如果马上会再次调用updateConfiguration(),则忽略本次修改,交由下次处理,节省时间
// 如果当前Activity已经finish,则忽略
// 如果Activity的状态是DESTROYED,则忽略
// 如果Activity不可见。则忽略
...
// 如果新旧配置相同,则忽略
mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
return true;
}
// 计算哪些配置值发生了改变
final int changes = getConfigurationChanges(mTmpConfig);
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
// Activity状态是INITIALIZING,则忽略
...
if (changes == 0 && !forceNewConfig) {
...
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
scheduleConfigurationChanged(newMergedOverrideConfig);
}
notifyDisplayCompatPolicyAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
return true;
}
...
boolean shouldRelaunchLocked = shouldRelaunchLocked(changes, mTmpConfig); // 1
...
if (shouldRelaunchLocked || forceNewConfig) { // 重启Activity
...
if (mState == PAUSING) {
...
// 如果当前Activity处于PAUSING状态,则标记其需要重启,等到PAUSING后reLaunch
deferRelaunchUntilPaused = true;
preserveWindowOnDeferredRelaunch = preserveWindow;
return true;
} else {
...
relaunchActivityLocked(preserveWindow);
}
// All done... tell the caller we weren't able to keep this activity around.
return false;
}
// Activity可以自己处理配置变更走这里
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
scheduleConfigurationChanged(newMergedOverrideConfig);
}
...
return true;
}
决定是否需要 reLaunch 的关键参数是 shouldRelaunchLocked 和 forceNewConfig,任一值为 true,就会重启 Activity,而不会调用 Activity#onConfigurationChanged()。
注释1处 shouldRelaunchLocked() 会通过对比事件是否在 Activity 自己可以处理的范围内来决定是否 reLaunch,其中包括 AndroidManifest.xml 中配置的 android:configChanges 属性。
-
重启 Activity 分支
执行
ActivityRecord#relaunchActivityLocked(),经过层层调用,最终调用到ActivityThread#handleRelaunchActivityInner()。javaframeworks/base/core/java/android/app/ActivityThread.java private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents, PendingTransactionActions pendingActions, boolean startsNotResumed, Configuration overrideConfig, String reason) { ... handleDestroyActivity(r, false, configChanges, true, reason); ... handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent); }在
handleRelaunchActivityInner()中,先调用handleDestroyActivity()销毁当前 Activity,然后调用handleLaunchActivity()重启 Activity。Activity 有一个回调方法onRetainNonConfigurationInstance(),当设备信息变更时,会保存该方法返回的 Object,之后可以在重启的 Activity 中通过getLastNonConfigurationInstance()获取该 Object。 -
不重启 Activity 分支
javaframeworks/base/services/core/java/com/android/server/wm/ActivityRecord.java boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow, boolean ignoreVisibility, boolean isRequestedOrientationChanged) { ... // Activity可以自己处理配置变更走这里 if (displayChanged) { scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig); } else { scheduleConfigurationChanged(newMergedOverrideConfig); } ... }这两个分支最终都会调用
ActivityThread#handleActivityConfigurationChanged()方法。javaframeworks/base/core/java/android/app/ActivityThread.java void handleActivityConfigurationChanged(ActivityClientRecord r, @NonNull Configuration overrideConfig, int displayId, boolean alwaysReportChange) { ... final Configuration reportedConfig = performConfigurationChangedForActivity(r, mConfigurationController.getCompatConfiguration(), movedToDifferentDisplay ? displayId : r.activity.getDisplayId(), alwaysReportChange); ... }调用
performConfigurationChangedForActivity()。javaframeworks/base/core/java/android/app/ActivityThread.java private Configuration performConfigurationChangedForActivity(ActivityClientRecord r, Configuration newBaseConfig, int displayId, boolean alwaysReportChange) { r.tmpConfig.setTo(newBaseConfig); if (r.overrideConfig != null) { r.tmpConfig.updateFrom(r.overrideConfig); } final Configuration reportedConfig = performActivityConfigurationChanged(r, r.tmpConfig, r.overrideConfig, displayId, alwaysReportChange); freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig)); return reportedConfig; }调用
performActivityConfigurationChanged()。javaprivate Configuration performActivityConfigurationChanged(ActivityClientRecord r, Configuration newConfig, Configuration amOverrideConfig, int displayId, boolean alwaysReportChange) { final Activity activity = r.activity; ... final Configuration currentResConfig = activity.getResources().getConfiguration(); final int diff = currentResConfig.diffPublicOnly(newConfig); final boolean hasPublicResConfigChange = diff != 0; // TODO(b/173090263): Use diff instead after the improvement of AssetManager and // ResourcesImpl constructions. final boolean shouldUpdateResources = hasPublicResConfigChange || shouldUpdateResources(activityToken, currentResConfig, newConfig, amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange); final boolean shouldReportChange = shouldReportChange( activity.mCurrentConfig, newConfig, r.mSizeConfigurations, activity.mActivityInfo.getRealConfigChanged(), alwaysReportChange); // Nothing significant, don't proceed with updating and reporting. if (!shouldUpdateResources && !shouldReportChange) { return null; } ... final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig, contextThemeWrapperOverrideConfig); ... activity.mConfigChangeFlags = 0; if (shouldReportChange) { activity.mCalled = false; activity.mCurrentConfig = new Configuration(newConfig); activity.onConfigurationChanged(configToReport); // 1 if (!activity.mCalled) { throw new SuperNotCalledException("Activity " + activity.getLocalClassName() + " did not call through to super.onConfigurationChanged()"); } } return configToReport; }注释1处调用
Activity.onConfigurationChanged()。
总结:设备配置发生变更时,系统会根据一系列条件决定是重启 Activity,还是调用 Activity.onConfigurationChanged()。