http://blog.csdn.net/siobhan/article/details/8564798
这篇文章继续当旋转手机的时候,系统会做什么操作去通知Activity要旋转界面了。
在上一篇文章中我们看到如果我们在Settings选中了“Auto-rotate”的时候,PhoneWindowManager调用updateOrientationListenerLp去SensorManager里面去注册一个Listener,这样当Sensor发生变化的时候,PhoneWindowManager就可以监听到并且调用回调onProposeRotationChanged。
1. MyOrientationListener.onProposeRotationChange
- class MyOrientationListener extends WindowOrientationListener {
- @Override
- public void onProposedRotationChanged(int rotation) {
- if (localLOGV) Log.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
- updateRotation(false);
- }
- }
2. PhoneWindowManager.updateRotation(boolean alwaysSendConfiguration)
注意上面传下来的alwaysSendConfiguration是false。
- void updateRotation(boolean alwaysSendConfiguration) {
- try {
- //set orientation on WindowManager
- mWindowManager.updateRotation(alwaysSendConfiguration, false);
- } catch (RemoteException e) {
- // Ignore
- }
- }
3. WindowManagerService.updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout)
alwaysSendConfiguration = false;
forceRelayout = false;
updateRotation中直接调用了updateRotatinUnchecked(false, false);
1. 在updateRotatinUnchecked中先通过updateRotationUncheckedLocked(false) 去检测是否Rotation发生改变了。
2. performLayoutAndPlaceSurfacesLocked(); //这个函数太复杂....
3. 如果Rotation发生变化了或者alwaysSendConfiguration为true,就sendNewConfiguration给AMS。
- public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
- updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
- }
- public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
- boolean changed;
- synchronized(mWindowMap) {
- changed = updateRotationUncheckedLocked(false);
- if (!changed || forceRelayout) {
- getDefaultDisplayContentLocked().layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
- }
- }
- if (changed || alwaysSendConfiguration) {
- sendNewConfiguration();
- }
- ... ...
- }
3.1 WindowManagerService.updateRotationUncheckedLocked(boolean inTransaction) // inTrasaction = false
1. 请求mPolicy.rotationForOrientationLw去获取当前的rotation
2. 如果发现当然的mRotation与刚才取出来的rotation是一样的并且mAltOrientation跟刚才取出来的altOrientation是一样就返回false
要不然就 rotation,altOrientation放在mRotation和mAltOrientation 中保存起来;
3. mPolicy.setRotationLw(mRotation); 把mRotation保存在mOrientationListener的mCurrentRotation中。
4. computeScreenConfigurationLocked()调用这个函数主要是更新Screen size的信息,包括Rotation啊,width,height...
5. mDisplayManagerService.performTraversalInTransactionFromWindowManager();
6. 遍历所有windows,把有Surface的Window的mOrientationChanging设为true.
- public boolean updateRotationUncheckedLocked(boolean inTransaction) {
- ... ...
- int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);
- boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
- mForcedAppOrientation, rotation);
- ... ...
- if (mRotation == rotation && mAltOrientation == altOrientation) {
- // No change.
- return false;
- }
- ... ...
- mRotation = rotation;
- mAltOrientation = altOrientation;
- mPolicy.setRotationLw(mRotation);
- mWindowsFreezingScreen = true;
- mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
- mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
- WINDOW_FREEZE_TIMEOUT_DURATION);
- mWaitingForConfig = true;
- getDefaultDisplayContentLocked().layoutNeeded = true;
- startFreezingDisplayLocked(inTransaction, 0, 0);
- // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
- screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
- // We need to update our screen size information to match the new
- // rotation. Note that this is redundant with the later call to
- // sendNewConfiguration() that must be called after this function
- // returns... however we need to do the screen size part of that
- // before then so we have the correct size to use when initializing
- // the rotation animation for the new rotation.
- computeScreenConfigurationLocked(null);
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (!inTransaction) {
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked");
- }
- Surface.openTransaction();
- }
- try {
- // NOTE: We disable the rotation in the emulator because
- // it doesn't support hardware OpenGL emulation yet.
- if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
- && screenRotationAnimation.hasScreenshot()) {
- if (screenRotationAnimation.setRotationInTransaction(
- rotation, mFxSession,
- MAX_ANIMATION_DURATION, mTransitionAnimationScale,
- displayInfo.logicalWidth, displayInfo.logicalHeight)) {
- updateLayoutToAnimationLocked();
- }
- }
- mDisplayManagerService.performTraversalInTransactionFromWindowManager();
- } finally {
- if (!inTransaction) {
- Surface.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked");
- }
- }
- }
- final WindowList windows = displayContent.getWindowList();
- for (int i = windows.size() - 1; i >= 0; i--) {
- WindowState w = windows.get(i);
- if (w.mHasSurface) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
- w.mOrientationChanging = true;
- mInnerFields.mOrientationChangeComplete = false;
- }
- }
- for (int i=mRotationWatchers.size()-1; i>=0; i--) {
- try {
- mRotationWatchers.get(i).onRotationChanged(rotation);
- } catch (RemoteException e) {
- }
- }
- scheduleNotifyRotationChangedIfNeededLocked(displayContent, rotation);
- return true;
- }
3.1.1 PhoneWindowManager.rotationForOrientationLw(int orientation, int lastRotation)
1. 通过mOrientationListener.getPropsoeRotation() 从Sensor那边获取建议的Rotation,返回给sensorRotation.
2. 定义一个perferredRotation,然后根据一系列的判断条件找到一个符合条件的Rotation赋值给perferredRotation,
3. 然后再根据传进来的应用程序自己设定的orientation,如果有设定强制哪种横竖屏方式就直接返回与横竖屏对应的Rotation,如果没有强制设定,默认就返回perferredRotation。
- public int rotationForOrientationLw(int orientation, int lastRotation) {
- ... ...
- synchronized (mLock) {
- int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
- if (sensorRotation < 0) {
- sensorRotation = lastRotation;
- }
- final int preferredRotation;
- if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
- // Ignore sensor when lid switch is open and rotation is forced.
- preferredRotation = mLidOpenRotation;
- } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
- && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
- // Ignore sensor when in car dock unless explicitly enabled.
- // This case can override the behavior of NOSENSOR, and can also
- // enable 180 degree rotation while docked.
- preferredRotation = mCarDockEnablesAccelerometer
- ? sensorRotation : mCarDockRotation;
- } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
- && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
- // Ignore sensor when in desk dock unless explicitly enabled.
- // This case can override the behavior of NOSENSOR, and can also
- // enable 180 degree rotation while docked.
- preferredRotation = mDeskDockEnablesAccelerometer
- ? sensorRotation : mDeskDockRotation;
- } else if (mHdmiPlugged && mHdmiRotationLock) {
- // Ignore sensor when plugged into HDMI.
- // Note that the dock orientation overrides the HDMI orientation.
- preferredRotation = mHdmiRotation;
- } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
- && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
- || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED))
- || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
- || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
- // Otherwise, use sensor only if requested by the application or enabled
- // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
- if (mAllowAllRotations < 0) {
- // Can't read this during init() because the context doesn't
- // have display metrics at that time so we cannot determine
- // tablet vs. phone then.
- mAllowAllRotations = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
- }
- if (sensorRotation != Surface.ROTATION_180
- || mAllowAllRotations == 1
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR) {
- preferredRotation = sensorRotation;
- } else {
- preferredRotation = lastRotation;
- }
- } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
- && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
- // Apply rotation lock. Does not apply to NOSENSOR.
- // The idea is that the user rotation expresses a weak preference for the direction
- // of gravity and as NOSENSOR is never affected by gravity, then neither should
- // NOSENSOR be affected by rotation lock (although it will be affected by docks).
- preferredRotation = mUserRotation;
- } else {
- // No overriding preference.
- // We will do exactly what the application asked us to do.
- preferredRotation = -1;
- }
- switch (orientation) {
- case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
- // Return portrait unless overridden.
- if (isAnyPortrait(preferredRotation)) {
- return preferredRotation;
- }
- return mPortraitRotation;
- case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
- // Return landscape unless overridden.
- if (isLandscapeOrSeascape(preferredRotation)) {
- return preferredRotation;
- }
- return mLandscapeRotation;
- case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
- // Return reverse portrait unless overridden.
- if (isAnyPortrait(preferredRotation)) {
- return preferredRotation;
- }
- return mUpsideDownRotation;
- case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
- // Return seascape unless overridden.
- if (isLandscapeOrSeascape(preferredRotation)) {
- return preferredRotation;
- }
- return mSeascapeRotation;
- case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
- // Return either landscape rotation.
- if (isLandscapeOrSeascape(preferredRotation)) {
- return preferredRotation;
- }
- if (isLandscapeOrSeascape(lastRotation)) {
- return lastRotation;
- }
- return mLandscapeRotation;
- case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
- // Return either portrait rotation.
- if (isAnyPortrait(preferredRotation)) {
- return preferredRotation;
- }
- if (isAnyPortrait(lastRotation)) {
- return lastRotation;
- }
- return mPortraitRotation;
- default:
- // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
- // just return the preferred orientation we already calculated.
- if (preferredRotation >= 0) {
- return preferredRotation;
- }
- return Surface.ROTATION_0;
- }
- }
3.2 WindowManagerService.performLayoutAndPlaceSurfacesLocked()
1. 在performLayoutAndPlaceSurfacesLocked中就调用了一次performLayoutAndPlaceSurfacesLockedLoop()
- private final void performLayoutAndPlaceSurfacesLocked() {
- int loopCount = 6;
- do {
- mTraversalScheduled = false;
- performLayoutAndPlaceSurfacesLockedLoop();
- mH.removeMessages(H.DO_TRAVERSAL);
- loopCount--;
- } while (mTraversalScheduled && loopCount > 0);
- }
3.3 WindowManagerService.sendNewConfiguration()
1. 去调AMS的updateConfiguration.
- void sendNewConfiguration() {
- try {
- mActivityManager.updateConfiguration(null);
- } catch (RemoteException e) {
- }
- }
3.3.1 updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean persistent, boolean initLocale)
先通过mWindowManager.computeNewConfiguration(); 去获取WMS中最新的configuration,然后通过mSystemThread.applyConfigurationToResources(configCopy);去更新AMS所在进程资源。
1. 通过app.thread.scheduleConfigurationChanged(configCopy); 给让每个进程去update configuration
2. 发送一个Intent.ACTION_CONFIGURATION_CHANGED的broadcast出去。
3. starting = mMainStack.topRunningActivityLocked(null);
kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
mMainStack.ensureActivitiesVisibleLocked(starting, changes);
- boolean updateConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean persistent, boolean initLocale) {
- // do nothing if we are headless
- if (mHeadless) return true;
- int changes = 0;
- boolean kept = true;
- if (values != null) {
- Configuration newConfig = new Configuration(mConfiguration);
- changes = newConfig.updateFrom(values);
- if (changes != 0) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
- Slog.i(TAG, "Updating configuration to: " + values);
- }
- EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
- if (values.locale != null && !initLocale) {
- saveLocaleLocked(values.locale,
- !values.locale.equals(mConfiguration.locale),
- values.userSetLocale);
- }
- mConfigurationSeq++;
- if (mConfigurationSeq <= 0) {
- mConfigurationSeq = 1;
- }
- newConfig.seq = mConfigurationSeq;
- mConfiguration = newConfig;
- Slog.i(TAG, "Config changed: " + newConfig);
- final Configuration configCopy = new Configuration(mConfiguration);
- // TODO: If our config changes, should we auto dismiss any currently
- // showing dialogs?
- mShowDialogs = shouldShowDialogs(newConfig);
- 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);
- mHandler.sendMessage(msg);
- }
- 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, "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, 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);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- }
- }
- }
- 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 = mMainStack.topRunningActivityLocked(null);
- }
- if (starting != null) {
- kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- mMainStack.ensureActivitiesVisibleLocked(starting, changes);
- }
- if (values != null && mWindowManager != null) {
- mWindowManager.setNewConfiguration(mConfiguration);
- }
- return kept;
- }
3.3.1.1 ActivityThread.handleConfigurationChanged()
1. applyConfigurationToResourcesLocked(config, compat); // 更新该进程的资源相关的东西
2. performConfigurationChanged() // 调用callback的onConfigurationChanged
- final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
- int configDiff = 0;
- synchronized (mPackages) {
- ... ...
- applyConfigurationToResourcesLocked(config, compat);
- if (mConfiguration == null) {
- mConfiguration = new Configuration();
- }
- if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
- return;
- }
- configDiff = mConfiguration.diff(config);
- mConfiguration.updateFrom(config);
- config = applyCompatConfiguration(mCurDefaultDisplayDpi);
- }
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
- ... ...
- if (callbacks != null) {
- final int N = callbacks.size();
- for (int i=0; i<N; i++) {
- performConfigurationChanged(callbacks.get(i), config);
- }
- }
- }
3.3.1.3 ActivityStack.ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges)
- final boolean ensureActivityConfigurationLocked(ActivityRecord r,
- int globalChanges) {
- if (mConfigWillChange) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Skipping config check (will change): " + r);
- return true;
- }
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Ensuring correct configuration: " + r);
- // Short circuit: if the two configurations are the exact same
- // object (the common case), then there is nothing to do.
- Configuration newConfig = mService.mConfiguration;
- if (r.configuration == newConfig && !r.forceNewConfig) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration unchanged in " + r);
- return true;
- }
- // We don't worry about activities that are finishing.
- if (r.finishing) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter in finishing " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
- // Okay we now are going to make this activity have the new config.
- // But then we need to figure out how it needs to deal with that.
- Configuration oldConfig = r.configuration;
- r.configuration = newConfig;
- // Determine what has changed. May be nothing, if this is a config
- // that has come back from the app after going idle. In that case
- // we just want to leave the official config object now in the
- // activity and do nothing else.
- final int changes = oldConfig.diff(newConfig);
- if (changes == 0 && !r.forceNewConfig) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration no differences in " + r);
- return true;
- }
- // If the activity isn't currently running, just leave the new
- // configuration and it will pick that up next time it starts.
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter not running " + r);
- r.stopFreezingScreenLocked(false);
- r.forceNewConfig = false;
- return true;
- }
- // Figure out how to handle the changes between the configurations.
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
- + Integer.toHexString(changes) + ", handles=0x"
- + Integer.toHexString(r.info.getRealConfigChanged())
- + ", newConfig=" + newConfig);
- }
- if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {
- // Aha, the activity isn't handling the change, so DIE DIE DIE.
- r.configChangeFlags |= changes;
- r.startFreezingScreenLocked(r.app, globalChanges);
- r.forceNewConfig = false;
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Config is destroying non-running " + r);
- destroyActivityLocked(r, true, false, "config");
- } else if (r.state == ActivityState.PAUSING) {
- // A little annoying: we are waiting for this activity to
- // finish pausing. Let's not do anything now, but just
- // flag that it needs to be restarted when done pausing.
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Config is skipping already pausing " + r);
- r.configDestroy = true;
- return true;
- } else if (r.state == ActivityState.RESUMED) {
- // Try to optimize this case: the configuration is changing
- // and we need to restart the top, resumed activity.
- // Instead of doing the normal handshaking, just say
- // "restart!".
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Config is relaunching resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true);
- r.configChangeFlags = 0;
- } else {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Config is relaunching non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false);
- r.configChangeFlags = 0;
- }
- // All done... tell the caller we weren't able to keep this
- // activity around.
- return false;
- }
- // Default case: the activity can handle this new configuration, so
- // hand it over. Note that we don't need to give it the new
- // configuration, since we always send configuration changes to all
- // process when they happen so it can just use whatever configuration
- // it last got.
- if (r.app != null && r.app.thread != null) {
- try {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
- r.app.thread.scheduleActivityConfigurationChanged(r.appToken);
- } catch (RemoteException e) {
- // If process died, whatever.
- }
- }
- r.stopFreezingScreenLocked(false);
- return true;