四、Activity的更新(旋轉) sendNewConfiguration()會調用到ActivityManagerService的updateConfiguration()來update Configuration,並根據應用的配置來判斷是否要重新lunch應用。 先看一下總體時序圖,後面詳細展 ...
四、Activity的更新(旋轉)
sendNewConfiguration()會調用到ActivityManagerService的updateConfiguration()來update Configuration,並根據應用的配置來判斷是否要重新lunch應用。
void sendNewConfiguration() { try { mActivityManager.updateConfiguration(null); } catch (RemoteException e) { } }
public void updateConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); synchronized(this) { if (values == null && mWindowManager != null) { // sentinel: fetch the current configuration from the window manager values = mWindowManager.computeNewConfiguration(); } if (mWindowManager != null) { mProcessList.applyDisplaySize(mWindowManager); } final long origId = Binder.clearCallingIdentity(); if (values != null) { Settings.System.clearConfiguration(values); } updateConfigurationLocked(values, null, false); Binder.restoreCallingIdentity(origId); } }
先看一下總體時序圖,後面詳細展開:
1)updateConfigurationLocked()
updateConfigurationLocked()
(1)獲取Configuration數據保存在mConfiguration
(2)調用ActivityThread的scheduleConfigurationChanged()
(3)發送ACTION_CONFIGURATION_CHANGED廣播
(4)獲取當前最上面活動的Activity,調用ActivityStack的ensureActivityConfigurationLocked()函數根據應用配置判斷是否要重新luncher應用
private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean initLocale, boolean persistent, int userId, boolean deferResume) { int changes = 0; if (mWindowManager != null) { mWindowManager.deferSurfaceLayout(); } if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); if (changes != 0) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION, "Updating configuration to: " + values); EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { final LocaleList locales = values.getLocales(); int bestLocaleIndex = 0; if (locales.size() > 1) { if (mSupportedSystemLocales == null) { mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales(); } bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales)); } SystemProperties.set("persist.sys.locale", locales.get(bestLocaleIndex).toLanguageTag()); LocaleList.setDefault(locales, bestLocaleIndex); mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, locales.get(bestLocaleIndex))); } mConfigurationSeq++; if (mConfigurationSeq <= 0) { mConfigurationSeq = 1; } newConfig.seq = mConfigurationSeq; mConfiguration = newConfig; Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig); mUsageStatsService.reportConfigurationChange(newConfig, mUserController.getCurrentUserIdLocked()); //mUsageStatsService.noteStartConfig(newConfig); final Configuration configCopy = new Configuration(mConfiguration); // TODO: If our config changes, should we auto dismiss any currently // showing dialogs? mShowDialogs = shouldShowDialogs(newConfig, mInVrMode); AttributeCache ac = AttributeCache.instance(); if (ac != null) { ac.updateConfiguration(configCopy); } // Make sure all resources in our process are updated // right now, so that anyone who is going to retrieve // resource values after we return will be sure to get // the new ones. This is especially important during // boot, where the first config change needs to guarantee // all resources have that config before following boot // code is executed. mSystemThread.applyConfigurationToResources(configCopy); if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) { Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); msg.obj = new Configuration(configCopy); msg.arg1 = userId; mHandler.sendMessage(msg); } final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; if (isDensityChange) { // Reset the unsupported display size dialog. mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG); killAllBackgroundProcessesExcept(Build.VERSION_CODES.N, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); } for (int i=mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); try { if (app.thread != null) { if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " + app.processName + " new config " + mConfiguration); app.thread.scheduleConfigurationChanged(configCopy); } } catch (Exception e) { } } Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); if (initLocale || !mProcessesReady) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } } // Update the configuration with WM first and check if any of the stacks need to be // resized due to the configuration change. If so, resize the stacks now and do any // relaunches if necessary. This way we don't need to relaunch again below in // ensureActivityConfigurationLocked(). if (mWindowManager != null) { final int[] resizedStacks = mWindowManager.setNewConfiguration(mConfiguration); if (resizedStacks != null) { for (int stackId : resizedStacks) { final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId); mStackSupervisor.resizeStackLocked( stackId, newBounds, null, null, false, false, deferResume); } } } } boolean kept = true; 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 = mainStack.ensureActivityConfigurationLocked(starting, changes, false); // 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); } } if (mWindowManager != null) { mWindowManager.continueSurfaceLayout(); } return kept; }
五、總結
總流程圖如下