Android6.0 旋轉屏幕(三)應用是否要重啓



上篇博客我們分析了WMS中的updateRotationUnchecked函數,當旋轉角度有變化時會調用sendNewConfiguration函數。這篇博客我們就來分析下這個函數。

  1. public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
  2. if(DEBUG_ORIENTATION) Slog.v(TAG, "updateRotationUnchecked("
  3. + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")");
  4. long origId = Binder.clearCallingIdentity();
  5. boolean changed;
  6. synchronized(mWindowMap) {
  7. changed = updateRotationUncheckedLocked(false);
  8. if (!changed || forceRelayout) {
  9. getDefaultDisplayContentLocked().layoutNeeded = true;
  10. performLayoutAndPlaceSurfacesLocked();
  11. }
  12. }
  13. if (changed || alwaysSendConfiguration) {
  14. sendNewConfiguration();
  15. }
  16. Binder.restoreCallingIdentity(origId);
  17. }

AMS的updateConfiguration函數

這個函數最後是調用了AMS的updateConfiguration函數。

  1. void sendNewConfiguration() {
  2. try {
  3. mActivityManager.updateConfiguration(null);
  4. } catch (RemoteException e) {
  5. }
  6. }
我們再來看AMS的updateConfiguration函數,這個時候參數values是空,所以會調用WMS的computeNewConfiguration來獲取一些配置信息。然後會調用updateConfigurationLocked函數。

  1. public void updateConfiguration(Configuration values) {
  2. enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
  3. "updateConfiguration()");
  4. synchronized(this) {
  5. if (values == null && mWindowManager != null) {
  6. // sentinel: fetch the current configuration from the window manager
  7. values = mWindowManager.computeNewConfiguration();
  8. }
  9. if (mWindowManager != null) {
  10. mProcessList.applyDisplaySize(mWindowManager);
  11. }
  12. final long origId = Binder.clearCallingIdentity();
  13. if (values != null) {
  14. Settings.System.clearConfiguration(values);
  15. }
  16. updateConfigurationLocked(values, null, false, false);
  17. Binder.restoreCallingIdentity(origId);
  18. }
  19. }

我們再來看updateConfigurationLocked函數,先把Configuration數據保存在mConfiguration,然後會調用ActivityThread的scheduleConfigurationChanged。緊接着會發送ACTION_CONFIGURATION_CHANGED廣播,然後獲取當前最上面活動的Activity,調用ActivityStack的ensureActivityConfigurationLocked函數和ActivityStackSupervisor的ensureActivitiesVisibleLocked函數。

  1. boolean updateConfigurationLocked(Configuration values,
  2. ActivityRecord starting, boolean persistent, boolean initLocale) {
  3. int changes = 0;
  4. if (values != null) {
  5. Configuration newConfig = new Configuration(mConfiguration);
  6. changes = newConfig.updateFrom(values);
  7. if (changes != 0) {
  8. if (!initLocale && values.locale != null && values.userSetLocale) {
  9. final String languageTag = values.locale.toLanguageTag();
  10. SystemProperties.set("persist.sys.locale", languageTag);
  11. mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
  12. values.locale));
  13. }
  14. mConfigurationSeq++;
  15. if (mConfigurationSeq <= 0) {
  16. mConfigurationSeq = 1;
  17. }
  18. newConfig.seq = mConfigurationSeq;
  19. mConfiguration = newConfig;//保存數據
  20. Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
  21. mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
  22. //mUsageStatsService.noteStartConfig(newConfig);
  23. final Configuration configCopy = new Configuration(mConfiguration);
  24. // TODO: If our config changes, should we auto dismiss any currently
  25. // showing dialogs?
  26. mShowDialogs = shouldShowDialogs(newConfig);
  27. AttributeCache ac = AttributeCache.instance();
  28. if (ac != null) {
  29. ac.updateConfiguration(configCopy);
  30. }
  31. mSystemThread.applyConfigurationToResources(configCopy);
  32. if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
  33. Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
  34. msg.obj = new Configuration(configCopy);
  35. mHandler.sendMessage(msg);
  36. }
  37. for (int i=mLruProcesses.size()-1; i>=0; i--) {
  38. ProcessRecord app = mLruProcesses.get(i);
  39. try {
  40. if (app.thread != null) {
  41. //調用ActivityThread的scheduleConfigurationChanged函數
  42. app.thread.scheduleConfigurationChanged(configCopy);
  43. }
  44. } catch (Exception e) {
  45. }
  46. }
  47. Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);//發送廣播
  48. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
  49. | Intent.FLAG_RECEIVER_REPLACE_PENDING
  50. | Intent.FLAG_RECEIVER_FOREGROUND);
  51. broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
  52. null, AppOpsManager.OP_NONE, null, false, false,
  53. MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
  54. if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
  55. intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
  56. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
  57. if (!mProcessesReady) {
  58. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
  59. }
  60. broadcastIntentLocked(null, null, intent,
  61. null, null, 0, null, null, null, AppOpsManager.OP_NONE,
  62. null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
  63. }
  64. }
  65. }
  66. boolean kept = true;
  67. final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
  68. // mainStack is null during startup.
  69. if (mainStack != null) {
  70. if (changes != 0 && starting == null) {
  71. // If the configuration changed, and the caller is not already
  72. // in the process of starting an activity, then find the top
  73. // activity to check if its configuration needs to change.
  74. starting = mainStack.topRunningActivityLocked(null);
  75. }
  76. if (starting != null) {
  77. kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
  78. // And we need to make sure at this point that all other activities
  79. // are made visible with the correct configuration.
  80. mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
  81. }
  82. }
  83. if (values != null && mWindowManager != null) {
  84. mWindowManager.setNewConfiguration(mConfiguration);
  85. }
  86. return kept;
  87. }

我們先來看看WMS的setNewConfiguration函數,主要是調用了performLayoutAndPlaceSurfacesLocked,這個函數主要是刷新系統UI和計算窗口等,我們在後續分析WMS的時候會詳細介紹。這裏就不分析了

  1. @Override
  2. public void setNewConfiguration(Configuration config) {
  3. if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
  4. "setNewConfiguration()")) {
  5. throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
  6. }
  7. synchronized(mWindowMap) {
  8. mCurConfiguration = new Configuration(config);
  9. if (mWaitingForConfig) {
  10. mWaitingForConfig = false;
  11. mLastFinishedFreezeSource = "new-config";
  12. }
  13. performLayoutAndPlaceSurfacesLocked();
  14. }
  15. }

ActivityThread的handleConfigurationChanged

ActivityThead最後會調用到handleConfigurationChanged函數,然後會調用collectComponentCallbacks函數來統計回調,後面就調用performConfigurationChanged函數。

  1. final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
  2. int configDiff = 0;
  3. synchronized (mResourcesManager) {
  4. if (mPendingConfiguration != null) {
  5. if (!mPendingConfiguration.isOtherSeqNewer(config)) {
  6. config = mPendingConfiguration;
  7. mCurDefaultDisplayDpi = config.densityDpi;
  8. updateDefaultDensity();
  9. }
  10. mPendingConfiguration = null;
  11. }
  12. if (config == null) {
  13. return;
  14. }
  15. mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
  16. if (mConfiguration == null) {
  17. mConfiguration = new Configuration();
  18. }
  19. if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
  20. return;
  21. }
  22. configDiff = mConfiguration.updateFrom(config);
  23. config = applyCompatConfiguration(mCurDefaultDisplayDpi);
  24. }
  25. ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
  26. freeTextLayoutCachesIfNeeded(configDiff);
  27. if (callbacks != null) {
  28. final int N = callbacks.size();
  29. for (int i=0; i<N; i++) {
  30. performConfigurationChanged(callbacks.get(i), config);
  31. }
  32. }
  33. }

最後在performConfigurationChanged中會調用回調的onConfigurationChanged函數。最後也會調用到應用的Activity的onConfigurationChanged函數中。

  1. private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
  2. // Only for Activity objects, check that they actually call up to their
  3. // superclass implementation. ComponentCallbacks2 is an interface, so
  4. // we check the runtime type and act accordingly.
  5. Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
  6. if (activity != null) {
  7. activity.mCalled = false;
  8. }
  9. boolean shouldChangeConfig = false;
  10. if ((activity == null) || (activity.mCurrentConfig == null)) {
  11. shouldChangeConfig = true;
  12. } else {
  13. // If the new config is the same as the config this Activity
  14. // is already running with then don't bother calling
  15. // onConfigurationChanged
  16. int diff = activity.mCurrentConfig.diff(config);
  17. if (diff != 0) {
  18. // If this activity doesn't handle any of the config changes
  19. // then don't bother calling onConfigurationChanged as we're
  20. // going to destroy it.
  21. if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
  22. shouldChangeConfig = true;
  23. }
  24. }
  25. }
  26. if (shouldChangeConfig) {
  27. cb.onConfigurationChanged(config);
  28. if (activity != null) {
  29. if (!activity.mCalled) {
  30. throw new SuperNotCalledException(
  31. "Activity " + activity.getLocalClassName() +
  32. " did not call through to super.onConfigurationChanged()");
  33. }
  34. activity.mConfigChangeFlags = 0;
  35. activity.mCurrentConfig = new Configuration(config);
  36. }
  37. }
  38. }

ActivityStack的ensureActivityConfigurationLocked函數

我們繼續回到AMS的updateConfigurationLocked函數,看如下代碼,先獲取最上面的ActivityRecord,然後調用ActivityStack的ensureActivityConfigurationLocked函數。

  1. if (mainStack != null) {
  2. if (changes != 0 && starting == null) {
  3. // If the configuration changed, and the caller is not already
  4. // in the process of starting an activity, then find the top
  5. // activity to check if its configuration needs to change.
  6. starting = mainStack.topRunningActivityLocked(null);
  7. }
  8. if (starting != null) {
  9. kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
  10. // And we need to make sure at this point that all other activities
  11. // are made visible with the correct configuration.
  12. mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
  13. }
  14. }

ensureActivityConfigurationLocked函數如下,先要滿足一定條件(需要在AndroidManifest.xml中配置下config change相關的配置,我們可以看下之前的博客http://blog.csdn.net/kc58236582/article/details/53665682)根據當前Activity狀態來調用relaunchActivityLocked(這個函數又會調用ActivityThread的scheduleRelaunchActivity函數重新啓動Activity)或者destroyActivityLocked函數。隨後調用ActivityThread的scheduleActivityConfigurationChanged。

  1. final boolean ensureActivityConfigurationLocked(ActivityRecord r,
  2. int globalChanges) {
  3. ......
  4. if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {//從這句判斷知道需要在AndroidManifest中配置的
  5. // Aha, the activity isn't handling the change, so DIE DIE DIE.
  6. r.configChangeFlags |= changes;
  7. r.startFreezingScreenLocked(r.app, globalChanges);
  8. r.forceNewConfig = false;
  9. if (r.app == null || r.app.thread == null) {
  10. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
  11. "Config is destroying non-running " + r);
  12. destroyActivityLocked(r, true, "config");
  13. } else if (r.state == ActivityState.PAUSING) {
  14. // A little annoying: we are waiting for this activity to
  15. // finish pausing. Let's not do anything now, but just
  16. // flag that it needs to be restarted when done pausing.
  17. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
  18. "Config is skipping already pausing " + r);
  19. r.configDestroy = true;
  20. return true;
  21. } else if (r.state == ActivityState.RESUMED) {
  22. // Try to optimize this case: the configuration is changing
  23. // and we need to restart the top, resumed activity.
  24. // Instead of doing the normal handshaking, just say
  25. // "restart!".
  26. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
  27. "Config is relaunching resumed " + r);
  28. relaunchActivityLocked(r, r.configChangeFlags, true);
  29. r.configChangeFlags = 0;
  30. } else {
  31. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
  32. "Config is relaunching non-resumed " + r);
  33. relaunchActivityLocked(r, r.configChangeFlags, false);
  34. r.configChangeFlags = 0;
  35. }
  36. // All done... tell the caller we weren't able to keep this
  37. // activity around.
  38. return false;
  39. }
  40. // Default case: the activity can handle this new configuration, so hand it over.
  41. // NOTE: We only forward the stack override configuration as the system level configuration
  42. // changes is always sent to all processes when they happen so it can just use whatever
  43. // system level configuration it last got.
  44. if (r.app != null && r.app.thread != null) {
  45. try {
  46. if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending new config to " + r);
  47. r.app.thread.scheduleActivityConfigurationChanged(
  48. r.appToken, new Configuration(mOverrideConfig));
  49. } catch (RemoteException e) {
  50. // If process died, whatever.
  51. }
  52. }
  53. r.stopFreezingScreenLocked(false);
  54. return true;
  55. }

這樣應用就會重新啓動等(完成隨系統旋轉)。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章