横屏机制

原文:https://blog.csdn.net/guoqifa29/article/details/40504189 (注解比较详细,可以辅助阅读源码,感谢原文作者)

启动横屏应用时的整个逻辑:首先会从WindowManagerService那边获取屏幕的方向,然后再设置到ActivityManagerService中来,最后再启动Window的显示逻辑。

这三个步骤分别对应下面这三个函数(横屏最重要的三个调用函数):

(1). WindowManagerService.updateRotationUncheckedLocked()

(2). ActivityManagerService.updateConfigurationLocked(config, r, false, false)

(3). WindowManagerService.setNewConfiguration(mConfiguration)

这三个函数是配套使用的。对于转屏应用,首先是

我们找一个具体的转屏场景来分析,启动横屏activity。先给出时序图:


上图的调用也验证了前面所说的转屏会走的三个步骤。注意一下调用updateOrientationFromAppTokens()函数时,会先调用r.mayFreezeScreenLocked(r.app)函数,这个函数判断该activity是否已经跟目标进程关联,并且关联的进程正常运行,如果满足,那么就将ActivityRecord.appToken作为参数。

step1、WMS.updateOrientationFromAppTokens()

  1. public Configuration updateOrientationFromAppTokens(  
  2.         Configuration currentConfig, IBinder freezeThisOneIfNeeded) {  
  3.     if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
  4.             "updateOrientationFromAppTokens()")) {  
  5.         throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
  6.     }  
  7.   
  8.     Configuration config = null;  
  9.     long ident = Binder.clearCallingIdentity();  
  10.   
  11.     synchronized(mWindowMap) {  
  12.         config = updateOrientationFromAppTokensLocked(currentConfig,  
  13.                 freezeThisOneIfNeeded);    
  14.     }  
  15.   
  16.     Binder.restoreCallingIdentity(ident);  
  17.     return config;  
  18. }  
函数简单调用updateOrientationFromAppTokensLocked(),注意传进来的参数,currentConfig是当前的config信息,freezeThisOneIfNeeded就是前面说的ActivityRecord.appToken,继续往下研究。

step2、updateOrientationFromAppTokensLocked()

  1. private Configuration updateOrientationFromAppTokensLocked(  
  2.         Configuration currentConfig, IBinder freezeThisOneIfNeeded) {  
  3.     Configuration config = null;  
  4.   
  5.     if (updateOrientationFromAppTokensLocked(false)) {   //①满足这个条件是很苛刻的:从函数名就可以看出来是从可见的应用窗口获取orientation,并且orientation与last orientation不同,同时还必须将new orientation成功update到WMS中,也就是updateRotationUncheckedLocked()也要返回TRUE;  
  6.         if (freezeThisOneIfNeeded != null) {  
  7.             AppWindowToken atoken = findAppWindowToken(freezeThisOneIfNeeded);  
  8.             if (atoken != null) {                        //②屏幕旋转,并且freezeThisOneIfNeeded不为null,那么调用startAppFreezingScreenLocked()冻结screen;  
  9.                 startAppFreezingScreenLocked(atoken, ActivityInfo.CONFIG_ORIENTATION);  
  10.             }  
  11.         }  
  12.         config = computeNewConfigurationLocked();      //③调用computeNewConfigurationLocked()计算config;  
  13.   
  14.     } else if (currentConfig != null) {                  //④什么时候会走这个逻辑呢,具体看step3和step4中return false情况,也就是说没有update new orientation到WMS中。包括,1.可见应用窗口orientation与上一次相同;2.orientation与上一次不同,但是前一次转屏动画还在播放;3.屏幕是灭屏状态;4.PhoneWindowManager策略类综合出的orientation跟上一次相同;;  
  15.         // No obvious action we need to take, but if our current     
  16.         // state mismatches the activity manager's, update it,  
  17.         // disregarding font scale, which should remain set to    
  18.         // the value of the previous configuration.  
  19.         mTempConfiguration.setToDefaults();              //⑤下面这些逻辑就是即使不从应用窗口更改orientation,还有其他config需要核对差异。  
  20.         mTempConfiguration.fontScale = currentConfig.fontScale;  
  21.         //Flyme Theme: save the theme flag.  
  22.         mTempConfiguration.themeChanged = currentConfig.themeChanged;  
  23.         //Flyme Theme: save the theme flag.  
  24.         if (computeScreenConfigurationLocked(mTempConfiguration)) {   
  25.             if (currentConfig.diff(mTempConfiguration) != 0) {  
  26.                 mWaitingForConfig = true;  
  27.                 final DisplayContent displayContent = getDefaultDisplayContentLocked();  
  28.                 displayContent.layoutNeeded = true;  
  29.                 int anim[] = new int[2];  
  30.                 if (displayContent.isDimming()) {  
  31.                     anim[0] = anim[1] = 0;  
  32.                 } else {  
  33.                     mPolicy.selectRotationAnimationLw(anim);  
  34.                 }  
  35.                 startFreezingDisplayLocked(false, anim[0], anim[1]);  
  36.                 config = new Configuration(mTempConfiguration);  
  37.             }  
  38.         }  
  39.     }  
  40.   
  41.     return config;  
  42. }  
step3、updateOrientationFromAppTokensLocked(false)
  1. /*  
  2.  * Determine the new desired orientation of the display, returning  
  3.  * a non-null new Configuration if it has changed from the current  
  4.  * orientation.  IF TRUE IS RETURNED SOMEONE MUST CALL  
  5.  * setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE  
  6.  * SCREEN.  This will typically be done for you if you call  
  7.  * sendNewConfiguration().  
  8.  *  
  9.  * The orientation is computed from non-application windows first. If none of  
  10.  * the non-application windows specify orientation, the orientation is computed from  
  11.  * application tokens.  
  12.  * @see android.view.IWindowManager#updateOrientationFromAppTokens(  
  13.  * android.os.IBinder)  
  14.  */  
  15. boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {   
  16.     long ident = Binder.clearCallingIdentity();  
  17.     try {  
  18.         int req = getOrientationFromWindowsLocked();         //①从非activity窗口中提取orientation;  
  19.         if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {  
  20.             req = getOrientationFromAppTokensLocked();        //②从activity窗口中提取orientation;  
  21.         }  
  22.   
  23.         if (req != mForcedAppOrientation) {              //③窗口设置的orientation与当前orientation不同,即更改orientation;  
  24.             mForcedAppOrientation = req;                 //④这个变量值得更改非常重要;  
  25.             //send a message to Policy indicating orientation change to take  
  26.             //action like disabling/enabling sensors etc.,  
  27.             mPolicy.setCurrentOrientationLw(req);        //⑤告诉PhoneWindowManager orientation,这样采取关闭或开启sensor;比如打开一个强制横屏的窗口,那么必然要关闭sensor嘛,如何关闭,自然是关闭sensor的listener了!  
  28.             if (updateRotationUncheckedLocked(inTransaction)) {   //⑥调用updateRotationUncheckedLocked()改变WMS侧屏幕方向,如果确实更新了orientation,那么返回TRUE,如果orientation没有更新,那自然返回false;  
  29.                 // changed  
  30.                 return true;  
  31.             }  
  32.         }  
  33.   
  34.         return false;  
  35.     } finally {  
  36.         Binder.restoreCallingIdentity(ident);  
  37.     }  
  38. }  

上面的解释的非常清楚这个函数是干嘛的,就是首先从非activity窗口中计算orientation,如果非activity窗口未指定orientation,那么接着从activity窗口中计算orientation。如果计算的orientation跟last不一样,那么首先调用PhoneWindowManager.setCurrentOrientationLw()打开或关闭sensor的listener;接着调用updateRotationUncheckedLocked()做出屏幕转变后WMS侧的处理逻辑。

上面逻辑中第④点中mForcedAppOrientation的赋值非常非常重要,为什么?因为当前启动的应用需要转屏,但是第⑥点中调用updateRotationUncheckedLocked()在很多场景下是无法update orientation的,比如前一个orientation 动画未播完或Rotation被Deferred等,难道就不update orientation了?当然不是,WMS这边在播完orientation动画、resumeRotation、亮屏、等一系列逻辑下会调用updateRotationUnchecked()函数,该函数会完成前面未完成的update orientation工作。看看updateRotationUnchecked()函数:

  1. public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {  
  2.     if(DEBUG_ORIENTATION) Slog.v(TAG, "updateRotationUnchecked("  
  3.                + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")");  
  4.   
  5.     long origId = Binder.clearCallingIdentity();  
  6.     boolean changed;  
  7.     synchronized(mWindowMap) {  
  8.         changed = updateRotationUncheckedLocked(false);  
  9.         if (!changed || forceRelayout) {  
  10.             getDefaultDisplayContentLocked().layoutNeeded = true;  
  11.             performLayoutAndPlaceSurfacesLocked();  
  12.         }  
  13.     }  
  14.   
  15.     if (changed || alwaysSendConfiguration) {  
  16.         sendNewConfiguration();  
  17.     }  
  18.   
  19.     Binder.restoreCallingIdentity(origId);  
  20. }  
调用updateRotationUncheckedLocked()函数继续完成WMS侧的update orientation工作,那updateRotationUncheckedLocked()怎么知道之前有过update orientation的需求?看看这个函数中调用mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);就清楚了,mForcedAppOrientation在前面重新赋值过了。哈哈,现在整个逻辑就清楚了,如果应用需要update new orientation,但是此刻WMS侧又无法update orientation成功,那么会在其他逻辑处理完成后调用updateRotationUnchecked()继续把update orientation工作做完,updateRotationUnchecked()函数调用updateRotationUncheckedLocked()更新orientation,如果确实更新了orientation,文章最开始就说了三个步骤缺一不可,第二个跟第三个步骤在哪调用呢?哈哈,就在sendNewConfiguration()中

  1. void sendNewConfiguration() {  
  2.     try {  
  3.         mActivityManager.updateConfiguration(null);  
  4.     } catch (RemoteException e) {  
  5.     }  
  6. }  
主动调用AMS的updateConfiguration()函数,updateConfiguration()函数中必然调用了第三个步骤:WindowManagerService.setNewConfiguration(mConfiguration)。

step4、updateRotationUncheckedLocked()

更新 new orientation 到WMS中来,如果更新失败,那么返回false,更新成功则返回true。如果更新失败,那么在其他逻辑完成后调用 updateRotationUnchecked()时会重新update orientation到WMS中来,正如step3中所说的mForcedAppOrientation是个关键变量,保存着request orientation。

  1. // TODO(multidisplay): Rotate any display?  
  2. /**  
  3.  * Updates the current rotation.  
  4.  *  
  5.  * Returns true if the rotation has been changed.  In this case YOU  
  6.  * MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN.  
  7.  */  
  8. public boolean updateRotationUncheckedLocked(boolean inTransaction) {  
  9.     if (mDeferredRotationPauseCount > 0) {      //①如果调用了pauseRotationLocked()来pauses rotation changes,那么mDeferredRotationPauseCount值会加1,此时便不能change rotation;待调用resumeRotationLocked()将mDeferredRotationPauseCount值减为0,便会调用updateRotationUncheckedLocked()再次change rotation;  
  10.         // Rotation updates have been paused temporarily.  Defer the update until  
  11.         // updates have been resumed.  
  12.         if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, rotation is paused.");  
  13.         return false;  
  14.     }  
  15.   
  16.     ScreenRotationAnimation screenRotationAnimation =  
  17.             mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);  
  18.     if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {   //②如果此刻正在做屏幕旋转动画,也是不能change rotation的;  
  19.         // Rotation updates cannot be performed while the previous rotation change  
  20.         // animation is still in progress.  Skip this update.  We will try updating  
  21.         // again after the animation is finished and the display is unfrozen.  
  22.         if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, animation in progress.");  
  23.         return false;  
  24.     }  
  25.   
  26.     if (!mDisplayEnabled) {         //③灭屏时也是不能change rotation的;  
  27.         // No point choosing a rotation if the display is not enabled.  
  28.         if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, display is not enabled.");  
  29.         return false;  
  30.     }  
  31.   
  32.     // TODO: Implement forced rotation changes.  
  33.     //       Set mAltOrientation to indicate that the application is receiving  
  34.     //       an orientation that has different metrics than it expected.  
  35.     //       eg. Portrait instead of Landscape.  
  36.   
  37.     int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);   //④根据mForcedAppOrientation和mRotation、传感器方向等值来综合考虑出orientation。mForcedAppOrientation保存着request orientation,mRotation是当前正在使用的屏幕方向。  
  38.     boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(  
  39.             mForcedAppOrientation, rotation);  
  40.   
  41.     if (DEBUG_ORIENTATION) {  
  42.         Slog.v(TAG, "Application requested orientation "  
  43.                 + mForcedAppOrientation + ", got rotation " + rotation  
  44.                 + " which has " + (altOrientation ? "incompatible" : "compatible")  
  45.                 + " metrics");  
  46.     }  
  47.   
  48.     if (mRotation == rotation && mAltOrientation == altOrientation) {         //⑤如果综合出来的orientation与last orientation相同,便无需update orientation;  
  49.         // No change.  
  50.         return false;  
  51.     }  
  52.   
  53.     if (DEBUG_ORIENTATION) {  
  54.         Slog.v(TAG,  
  55.             "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "")  
  56.             + " from " + mRotation + (mAltOrientation ? " (alt)" : "")  
  57.             + ", forceApp=" + mForcedAppOrientation);  
  58.     }  
  59.                                       //⑥以下逻辑是需要update orientation;  
  60.     mRotation = rotation;  
  61.     mAltOrientation = altOrientation;  
  62.     mPolicy.setRotationLw(mRotation);   //⑦将mRotation设置到PhoneWindowManager中;  
  63.   
  64.     mWindowsFreezingScreen = true;       //⑧准备冻结屏幕;  
  65.     mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);  
  66.     mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);  
  67.     mWaitingForConfig = true;  
  68.     final DisplayContent displayContent = getDefaultDisplayContentLocked();  
  69.     displayContent.layoutNeeded = true;        //⑨layoutNeeded为true;这样needsLayout()函数就可以返回true;  
  70.     final int[] anim = new int[2];  
  71.     if (displayContent.isDimming()) {  
  72.         anim[0] = anim[1] = 0;  
  73.     } else {  
  74.         mPolicy.selectRotationAnimationLw(anim);   //⑨PhoneWindowManager选择进入和退出转屏动画;        
  75.     }  
  76.     startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);     //⑨调用startFreezingDisplayLocked()冻结屏幕,参数中传入了进入和退出动画,这个函数在下面将详细分析;  
  77.     // startFreezingDisplayLocked can reset the ScreenRotationAnimation.  
  78.     screenRotationAnimation =  
  79.             mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);   //⑨获取DisplayContentsAnimator类对象,这个类中包含一个ScreenRotationAnimation类对象;  
  80.   
  81.     // We need to update our screen size information to match the new  
  82.     // rotation.  Note that this is redundant with the later call to  
  83.     // sendNewConfiguration() that must be called after this function  
  84.     // returns...  however we need to do the screen size part of that  
  85.     // before then so we have the correct size to use when initializing  
  86.     // the rotation animation for the new rotation.  
  87.     computeScreenConfigurationLocked(null);                       //⑨更新DisplayInfo信息;  
  88.   
  89.     final DisplayInfo displayInfo = displayContent.getDisplayInfo();  
  90.     if (!inTransaction) {  
  91.         if (SHOW_TRANSACTIONS) {  
  92.             Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked");  
  93.         }  
  94.         SurfaceControl.openTransaction();  
  95.     }  
  96.     try {  
  97.         // NOTE: We disable the rotation in the emulator because  
  98.         //       it doesn't support hardware OpenGL emulation yet.  
  99.         if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null  
  100.                 && screenRotationAnimation.hasScreenshot()) {         //⑨create Rotation Matrix and set Matrix to mSurfaceControl(截图surface)  
  101.             if (screenRotationAnimation.setRotationInTransaction(  
  102.                     rotation, mFxSession,  
  103.                     MAX_ANIMATION_DURATION, mTransitionAnimationScale,  
  104.                     displayInfo.logicalWidth, displayInfo.logicalHeight)) {  
  105.                 scheduleAnimationLocked();  
  106.             }  
  107.         }  
  108.   
  109.         mDisplayManagerService.performTraversalInTransactionFromWindowManager();   //⑨这个是干啥的??  
  110.     } finally {  
  111.         if (!inTransaction) {  
  112.             SurfaceControl.closeTransaction();  
  113.             if (SHOW_LIGHT_TRANSACTIONS) {  
  114.                 Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked");  
  115.             }  
  116.         }  
  117.     }  
  118.   
  119.     final WindowList windows = displayContent.getWindowList();  
  120.     for (int i = windows.size() - 1; i >= 0; i--) {                               //⑨对于未销毁surface的window,将WindowState.mOrientationChanging设为true;  
  121.         WindowState w = windows.get(i);  
  122.         if (w.mHasSurface  
  123.                 // FLYME_BEGIN  
  124.                 // FUNCTION:optimize the efficiency of rotating screen . added by fujinzhi. transplanted by duzhenhui.  
  125.                 /*&& !w.prohibitRotation()*/  
  126.                 // FLYME_END 2014.04.17  
  127.                 ) {  
  128.             if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);  
  129.             w.mOrientationChanging = true;  
  130.             mInnerFields.mOrientationChangeComplete = false;  
  131.         }  
  132.         w.mLastFreezeDuration = 0;  
  133.     }  
  134.   
  135.     for (int i=mRotationWatchers.size()-1; i>=0; i--) {                      //⑨将rotation发布到PhoneWindow、KeyguardFaceUnlockView、LegacySensorManager中去;重点是PhoneWindow,这个将在下面详细分析;  
  136.         try {  
  137.             mRotationWatchers.get(i).watcher.onRotationChanged(rotation);  
  138.         } catch (RemoteException e) {  
  139.         }  
  140.     }  
  141.   
  142.     //TODO (multidisplay): Magnification is supported only for the default display.  
  143.     if (mDisplayMagnifier != null  
  144.             && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {              //⑨如果打开手势放大,那么回调mDisplayMagnifier.onRotationChangedLocked()函数;  
  145.         mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);  
  146.     }  
  147.   
  148.     return true;  
  149. }  

这个函数是WMS侧的屏幕旋转逻辑主要处理函数,WMS侧几乎所有的屏幕旋转操作都在此函数中完成。AMS侧自然是updateConfigurationLocked()函数。

1)、PhoneWindowManager.setRotationLw(int rotation)将rotation传到PhoneWindowManager中去

  1. @Override  
  2. public void setRotationLw(int rotation) {  
  3.     mOrientationListener.setCurrentRotation(rotation);  
  4. }  
mOrientationListener是一个MyOrientationListener extends WindowOrientationListener类对象,rotation保存在mCurrentRotation中:
  1. /**  
  2.  * Sets the current rotation.  
  3.  *  
  4.  * @param rotation The current rotation.  
  5.  */  
  6. public void setCurrentRotation(int rotation) {  
  7.     synchronized (mLock) {  
  8.         mCurrentRotation = rotation;          
  9.     }  
  10. }  

2)、startFreezingDisplayLocked(inTransaction, anim[0], anim[1])函数,现在来详细研究下这个函数。

  1. private void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) {  
  2.     if (mDisplayFrozen) {           //①mDisplayFrozen变量为true,表示正在调用startFreezingDisplayLocked()冻结屏幕,并且还未调用stopFreezingDisplayLocked()进行解冻;  
  3.         return;  
  4.     }  
  5.   
  6.     if (!mDisplayReady || !mPolicy.isScreenOnFully()) {     //②系统还未ready或灭屏状态(PhoneWindowManager.mScreenOnFully==false);  
  7.         // No need to freeze the screen before the system is ready or if  
  8.         // the screen is off.  
  9.         return;  
  10.     }  
  11.   
  12.     mScreenFrozenLock.acquire();           //③获得一把唤醒锁,防止freeze screen期间系统灭屏并锁屏,锁屏会更改orientation;  
  13.   
  14.     mDisplayFrozen = true;  
  15.     mDisplayFreezeTime = SystemClock.elapsedRealtime();  
  16.     mLastFinishedFreezeSource = null;  
  17.   
  18.     mInputMonitor.freezeInputDispatchingLw();       //④freeze Input Dispatcher,也就是禁止触摸时间上报;  
  19.   
  20.     // Clear the last input window -- that is just used for  
  21.     // clean transitions between IMEs, and if we are freezing  
  22.     // the screen then the whole world is changing behind the scenes.  
  23.     mPolicy.setLastInputMethodWindowLw(null, null);      
  24.   
  25.     if (mAppTransition.isTransitionSet()) {        //④如果设置了activity切换动画,那么调用mAppTransition.freeze()设置mAppTransitionState=APP_STATE_READY;  
  26.         mAppTransition.freeze();  
  27.     }  
  28.   
  29.     if (PROFILE_ORIENTATION) {  
  30.         File file = new File("/data/system/frozen");  
  31.         Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);  
  32.     }  
  33.   
  34.     if (CUSTOM_SCREEN_ROTATION) {  
  35.         mExitAnimId = exitAnim;  
  36.         mEnterAnimId = enterAnim;  
  37.         final DisplayContent displayContent = getDefaultDisplayContentLocked();  
  38.         final int displayId = displayContent.getDisplayId();  
  39.         ResSchedulerManager mResSchedulerManager;  
  40.         ScreenRotationAnimation screenRotationAnimation =  
  41.                 mAnimator.getScreenRotationAnimationLocked(displayId);       
  42.         if (screenRotationAnimation != null) {                //⑤如果上次转屏动画还在播放,那么kill掉;  
  43.             screenRotationAnimation.kill();  
  44.         }  
  45.         mResSchedulerManager = (ResSchedulerManager) mContext  
  46. .getSystemService(Context.RES_SCHEDULER_SERVICE);  
  47.         mResSchedulerManager.dispatchSwitchPerfModeCMD(mResSchedulerManager.APP_ROTATE);  
  48.   
  49.         // TODO(multidisplay): rotation on main screen only.  
  50.         screenRotationAnimation = new ScreenRotationAnimation(this,mContext, displayContent,  
  51.                 mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(),mDisableScreenRotationForHdmi);  
  52.         mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);        //⑥设置一个ScreenRotationAnimation对象到DisplayContentsAnimator.mScreenRotationAnimation中;  
  53.     }  
  54. }  

总结起来这个函数就是静止灭屏、冻结触摸事件、创建ScreenRotationAnimation对象。难道这就是冻结屏幕???

3)、computeScreenConfigurationLocked(),来分析下。

  1. boolean computeScreenConfigurationLocked(Configuration config) {  
  2.     if (!mDisplayReady) {  
  3.         return false;  
  4.     }  
  5.   
  6.     // TODO(multidisplay): For now, apply Configuration to main screen only.  
  7.     final DisplayContent displayContent = getDefaultDisplayContentLocked();  
  8.   
  9.     // Use the effective "visual" dimensions based on current rotation  
  10.     final boolean rotated = (mRotation == Surface.ROTATION_90  
  11.             || mRotation == Surface.ROTATION_270);        //①根据mRotation值,计算出屏幕宽度和高度;mRotation值在是在调用该函数之前已经更新;  
  12.     final int realdw = rotated ?  
  13.             displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;  
  14.     final int realdh = rotated ?  
  15.             displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;  
  16.     int dw = realdw;  
  17.     int dh = realdh;  
  18.   
  19.     if (mAltOrientation) {  
  20.         if (realdw > realdh) {  
  21.             // Turn landscape into portrait.  
  22.             int maxw = (int)(realdh/1.3f);  
  23.             if (maxw < realdw) {  
  24.                 dw = maxw;  
  25.             }  
  26.         } else {  
  27.             // Turn portrait into landscape.  
  28.             int maxh = (int)(realdw/1.3f);  
  29.             if (maxh < realdh) {  
  30.                 dh = maxh;  
  31.             }  
  32.         }  
  33.     }  
  34.   
  35.     if (config != null) {  
  36.         config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :  
  37.                 Configuration.ORIENTATION_LANDSCAPE;  
  38.     }  
  39.   
  40.     // Update application display metrics.  
  41.     final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);  
  42.     final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);  
  43.     final DisplayInfo displayInfo = displayContent.getDisplayInfo();  
  44.     synchronized(displayContent.mDisplaySizeLock) {  
  45.         displayInfo.rotation = mRotation;  
  46.         displayInfo.logicalWidth = dw;  
  47.         displayInfo.logicalHeight = dh;  
  48.         displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;  
  49.         displayInfo.appWidth = appWidth;  
  50.         displayInfo.appHeight = appHeight;  
  51.         displayInfo.getLogicalMetrics(mRealDisplayMetrics,  
  52.                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);  
  53.         displayInfo.getAppMetrics(mDisplayMetrics);  
  54.         mDisplayManagerService.setDisplayInfoOverrideFromWindowManager(  
  55.                 displayContent.getDisplayId(), displayInfo);  
  56.     }  
  57.     if (false) {  
  58.         Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight);  
  59.     }  
  60.   
  61.     final DisplayMetrics dm = mDisplayMetrics;  
  62.     mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,  
  63.             mCompatDisplayMetrics);  
  64.   
  65.     if (config != null) {  
  66.         config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation)  
  67.                 / dm.density);  
  68.         config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)  
  69.                 / dm.density);  
  70.         computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, dm.density, config);  
  71.   
  72.         config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);  
  73.         config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);  
  74.         config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);  
  75.         config.densityDpi = displayContent.mBaseDisplayDensity;  
  76.   
  77.         // Update the configuration based on available input devices, lid switch,  
  78.         // and platform configuration.  
  79.         config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;  
  80.         config.keyboard = Configuration.KEYBOARD_NOKEYS;  
  81.         config.navigation = Configuration.NAVIGATION_NONAV;  
  82.   
  83.         int keyboardPresence = 0;  
  84.         int navigationPresence = 0;  
  85.         final InputDevice[] devices = mInputManager.getInputDevices();  
  86.         final int len = devices.length;  
  87.         for (int i = 0; i < len; i++) {  
  88.             InputDevice device = devices[i];  
  89.             if (!device.isVirtual()) {  
  90.                 final int sources = device.getSources();  
  91.                 final int presenceFlag = device.isExternal() ?  
  92.                         WindowManagerPolicy.PRESENCE_EXTERNAL :  
  93.                                 WindowManagerPolicy.PRESENCE_INTERNAL;  
  94.   
  95.                 if (mIsTouchDevice) {  
  96.                     if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==  
  97.                             InputDevice.SOURCE_TOUCHSCREEN) {  
  98.                         config.touchscreen = Configuration.TOUCHSCREEN_FINGER;  
  99.                     }  
  100.                 } else {  
  101.                     config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;  
  102.                 }  
  103.   
  104.                 if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {  
  105.                     config.navigation = Configuration.NAVIGATION_TRACKBALL;  
  106.                     navigationPresence |= presenceFlag;  
  107.                 } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD  
  108.                         && config.navigation == Configuration.NAVIGATION_NONAV) {  
  109.                     config.navigation = Configuration.NAVIGATION_DPAD;  
  110.                     navigationPresence |= presenceFlag;  
  111.                 }  
  112.   
  113.                 if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {  
  114.                     config.keyboard = Configuration.KEYBOARD_QWERTY;  
  115.                     keyboardPresence |= presenceFlag;  
  116.                 }  
  117.             }  
  118.         }  
  119.   
  120.         // Determine whether a hard keyboard is available and enabled.  
  121.         boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;  
  122.         if (hardKeyboardAvailable != mHardKeyboardAvailable) {  
  123.             mHardKeyboardAvailable = hardKeyboardAvailable;  
  124.             mHardKeyboardEnabled = hardKeyboardAvailable;  
  125.             mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);  
  126.             mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);  
  127.         }  
  128.         if (!mHardKeyboardEnabled) {  
  129.             config.keyboard = Configuration.KEYBOARD_NOKEYS;  
  130.         }  
  131.   
  132.         // Let the policy update hidden states.  
  133.         config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;  
  134.         config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;  
  135.         config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;  
  136.         mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);  
  137.     }  
  138.   
  139.     return true;  
  140. }  
总结:这个函数其实就是根据mRotation值更新DisplayInfo、config.orientation值。

step5、AMS.updateConfigurationLocked()

  1. /**  
  2.  * Do either or both things: (1) change the current configuration, and (2)  
  3.  * make sure the given activity is running with the (now) current  
  4.  * configuration.  Returns true if the activity has been left running, or  
  5.  * false if <var>starting</var> is being destroyed to match the new  
  6.  * configuration.  
  7.  * @param persistent TODO  
  8.  */  
  9. boolean updateConfigurationLocked(Configuration values,  
  10.         ActivityRecord starting, boolean persistent, boolean initLocale) {  
  11.     // do nothing if we are headless  
  12.     if (mHeadless) return true;  
  13.   
  14.     int changes = 0;  
  15.   
  16.     if (values != null) {  
  17.         Configuration newConfig = new Configuration(mConfiguration);  
  18.         changes = newConfig.updateFrom(values);  
  19.         if (changes != 0) {  
  20.             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {  
  21.                 Slog.i(TAG, "Updating configuration to: " + values);  
  22.             }  
  23.               
  24.             EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);  
  25.   
  26.             if (values.locale != null && !initLocale) {  
  27.                 saveLocaleLocked(values.locale,   
  28.                                  !values.locale.equals(mConfiguration.locale),  
  29.                                  values.userSetLocale);  
  30.             }  
  31.   
  32.             mConfigurationSeq++;  
  33.             if (mConfigurationSeq <= 0) {  
  34.                 mConfigurationSeq = 1;  
  35.             }  
  36.             newConfig.seq = mConfigurationSeq;  
  37.             mConfiguration = newConfig;                                //①将mConfiguration更新到最新config;  
  38.             Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);  
  39.   
  40.             final Configuration configCopy = new Configuration(mConfiguration);  
  41.               
  42.             // TODO: If our config changes, should we auto dismiss any currently  
  43.             // showing dialogs?  
  44.             mShowDialogs = shouldShowDialogs(newConfig);  
  45.   
  46.             AttributeCache ac = AttributeCache.instance();  
  47.             if (ac != null) {  
  48.                 ac.updateConfiguration(configCopy);  
  49.             }  
  50.   
  51.             // Make sure all resources in our process are updated  
  52.             // right now, so that anyone who is going to retrieve  
  53.             // resource values after we return will be sure to get  
  54.             // the new ones.  This is especially important during  
  55.             // boot, where the first config change needs to guarantee  
  56.             // all resources have that config before following boot  
  57.             // code is executed.  
  58.             mSystemThread.applyConfigurationToResources(configCopy);      //②将config更新到ResourcesManager中去;  
  59.   
  60.             if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {  
  61.                 Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);       //③post一个update消息,该消息处理函数中调用Settings.System.putConfiguration()保存config;  
  62.                 msg.obj = new Configuration(configCopy);  
  63.                 mHandler.sendMessage(msg);  
  64.             }  
  65.                 
  66.             for (int i=mLruProcesses.size()-1; i>=0; i--) {                 //④将横屏config通过调用ActivityThread.scheduleConfigurationChanged()传递每个应用进程中去,这个将在下面进行详细分析;  
  67.                 ProcessRecord app = mLruProcesses.get(i);  
  68.                 try {  
  69.                     if (app.thread != null) {  
  70.                         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "  
  71.                                 + app.processName + " new config " + mConfiguration);  
  72.                         app.thread.scheduleConfigurationChanged(configCopy);    
  73.                     }  
  74.                 } catch (Exception e) {  
  75.                 }  
  76.             }  
  77.             Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);  
  78.             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
  79.                     | Intent.FLAG_RECEIVER_REPLACE_PENDING  
  80.                     | Intent.FLAG_RECEIVER_FOREGROUND);  
  81.             broadcastIntentLocked(null, null, intent, null, null, 0, null, null,     //⑤发送一个config changed广播;  
  82.                     null, AppOpsManager.OP_NONE, false, false, MY_PID,  
  83.                     Process.SYSTEM_UID, UserHandle.USER_ALL);  
  84.             if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {  
  85.                 intent = new Intent(Intent.ACTION_LOCALE_CHANGED);  
  86.                 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);  
  87.                 broadcastIntentLocked(null, null, intent,  
  88.                         null, null, 0, null, null, null, AppOpsManager.OP_NONE,  
  89.                         false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);  
  90.             }  
  91.         }  
  92.     }  
  93.   
  94.     boolean kept = true;  
  95.     final ActivityStack mainStack = mStackSupervisor.getFocusedStack();      
  96.     if (changes != 0 && starting == null) {                                     //⑥如果start 横屏Activity时,参数starting就是正要启动的横屏Activity;如果是旋转屏幕,starting 就为null,此时把top Activity取出来;  
  97.         // If the configuration changed, and the caller is not already  
  98.         // in the process of starting an activity, then find the top  
  99.         // activity to check if its configuration needs to change.  
  100.         starting = mainStack.topRunningActivityLocked(null);  
  101.     }  
  102.   
  103.     if (starting != null) {  
  104.         kept = mainStack.ensureActivityConfigurationLocked(starting, changes);   //⑦将config设置到starting中去,下面会详细分析这个函数;  
  105.         // And we need to make sure at this point that all other activities  
  106.         // are made visible with the correct configuration.  
  107.         mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);       //⑧调用ensureActivitiesVisibleLocked()  
  108.     }  
  109.   
  110.     if (values != null && mWindowManager != null) {  
  111.         mWindowManager.setNewConfiguration(mConfiguration);                     //⑨调用WindowManagerService.setNewConfiguration()启动performLayoutAndPlaceSurfacesLocked()  
  112.     }  
  113.   
  114.     return kept;  
  115. }  
先来详细分析下第④点中将config传递到每个应用进程中去ActivityThread.scheduleConfigurationChanged(),这个函数调用ActivityThead.handleConfigurationChanged()进一步处理

  1. final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {  
  2.   
  3.     int configDiff = 0;  
  4.   
  5.     synchronized (mResourcesManager) {  
  6.         if (mPendingConfiguration != null) {  
  7.             if (!mPendingConfiguration.isOtherSeqNewer(config)) {  
  8.                 config = mPendingConfiguration;  
  9.                 mCurDefaultDisplayDpi = config.densityDpi;  
  10.                 updateDefaultDensity();  
  11.             }  
  12.             mPendingConfiguration = null;  
  13.         }  
  14.   
  15.         if (config == null) {  
  16.             return;  
  17.         }  
  18.           
  19.         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "  
  20.                 + config);  
  21.   
  22.         mResourcesManager.applyConfigurationToResourcesLocked(config, compat);      //①将config更新到应用进程的ResourcesManager中去  
  23.   
  24.         if (mConfiguration == null) {  
  25.             mConfiguration = new Configuration();  
  26.         }  
  27.         if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {  
  28.             return;  
  29.         }  
  30.         configDiff = mConfiguration.diff(config);  
  31.         mConfiguration.updateFrom(config);  
  32.         config = applyCompatConfiguration(mCurDefaultDisplayDpi);  
  33.     }  
  34.   
  35.     ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);  
  36.   
  37.     // Cleanup hardware accelerated stuff  
  38.     WindowManagerGlobal.getInstance().trimLocalMemory();  
  39.   
  40.     freeTextLayoutCachesIfNeeded(configDiff);  
  41.   
  42.     if (callbacks != null) {  
  43.         final int N = callbacks.size();  
  44.         for (int i=0; i<N; i++) {  
  45.             performConfigurationChanged(callbacks.get(i), config);  
  46.         }  
  47.     }  
  48. }  

再来分析下第⑦点中调用的ActivityStack.ensureActivityConfigurationLocked()函数。

  1. /**  
  2.  * Make sure the given activity matches the current configuration.  Returns  
  3.  * false if the activity had to be destroyed.  Returns true if the  
  4.  * configuration is the same, or the activity will remain running as-is  
  5.  * for whatever reason.  Ensures the HistoryRecord is updated with the  
  6.  * correct configuration and all other bookkeeping is handled.  
  7.  */  
  8. final boolean ensureActivityConfigurationLocked(ActivityRecord r,  
  9.         int globalChanges) {  
  10.     if (mConfigWillChange) {  
  11.         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  12.                 "Skipping config check (will change): " + r);  
  13.         return true;  
  14.     }  
  15.   
  16.     if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  17.             "Ensuring correct configuration: " + r);  
  18.   
  19.     // Short circuit: if the two configurations are the exact same  
  20.     // object (the common case), then there is nothing to do.  
  21.     Configuration newConfig = mService.mConfiguration;  
  22.     if (r.configuration == newConfig && !r.forceNewConfig) {  
  23.         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  24.                 "Configuration unchanged in " + r);  
  25.         return true;  
  26.     }  
  27.   
  28.     // We don't worry about activities that are finishing.  
  29.     if (r.finishing) {                                         //①正在finish的Activity则无需关心;  
  30.         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  31.                 "Configuration doesn't matter in finishing " + r);  
  32.         r.stopFreezingScreenLocked(false);  
  33.         return true;  
  34.     }  
  35.   
  36.     // Okay we now are going to make this activity have the new config.  
  37.     // But then we need to figure out how it needs to deal with that.  
  38.     Configuration oldConfig = r.configuration;  
  39.     r.configuration = newConfig;                              //②更新ActivityRecord.configuration值;   
  40.   
  41.     // Determine what has changed.  May be nothing, if this is a config  
  42.     // that has come back from the app after going idle.  In that case  
  43.     // we just want to leave the official config object now in the  
  44.     // activity and do nothing else.  
  45.     final int changes = oldConfig.diff(newConfig);  
  46.     if (changes == 0 && !r.forceNewConfig) {  
  47.         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  48.                 "Configuration no differences in " + r);  
  49.         return true;  
  50.     }  
  51.   
  52.     // If the activity isn't currently running, just leave the new  
  53.     // configuration and it will pick that up next time it starts.  
  54.     if (r.app == null || r.app.thread == null) {  
  55.         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  56.                 "Configuration doesn't matter not running " + r);  
  57.         r.stopFreezingScreenLocked(false);  
  58.         r.forceNewConfig = false;  
  59.         return true;  
  60.     }  
  61.   
  62.     // Figure out how to handle the changes between the configurations.  
  63.     if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {  
  64.         Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"  
  65.                 + Integer.toHexString(changes) + ", handles=0x"  
  66.                 + Integer.toHexString(r.info.getRealConfigChanged())  
  67.                 + ", newConfig=" + newConfig);  
  68.     }  
  69.     if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {  
  70.         // Aha,   
  71.         r.configChangeFlags |= changes;  
  72.         r.startFreezingScreenLocked(r.app, globalChanges);  
  73.         r.forceNewConfig = false;  
  74.         if (r.app == null || r.app.thread == null) {  
  75.             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  76.                     "Config is destroying non-running " + r);  
  77.             destroyActivityLocked(r, true, false, "config");  
  78.         } else if (r.state == ActivityState.PAUSING) {  
  79.             // A little annoying: we are waiting for this activity to  
  80.             // finish pausing.  Let's not do anything now, but just  
  81.             // flag that it needs to be restarted when done pausing.  
  82.             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  83.                     "Config is skipping already pausing " + r);  
  84.             r.configDestroy = true;  
  85.             return true;  
  86.         } else if (r.state == ActivityState.RESUMED) {  
  87.             // Try to optimize this case: the configuration is changing  
  88.             // and we need to restart the top, resumed activity.  
  89.             // Instead of doing the normal handshaking, just say  
  90.             // "restart!".  
  91.             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  92.                     "Config is relaunching resumed " + r);  
  93.             relaunchActivityLocked(r, r.configChangeFlags, true);  
  94.             r.configChangeFlags = 0;  
  95.         } else {  
  96.             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,  
  97.                     "Config is relaunching non-resumed " + r);  
  98.             relaunchActivityLocked(r, r.configChangeFlags, false);  
  99.             r.configChangeFlags = 0;  
  100.         }  
  101.   
  102.         // All done...  tell the caller we weren't able to keep this  
  103.         // activity around.  
  104.         return false;  
  105.     }  
  106.   
  107.     // Default case: the activity can handle this new configuration, so  
  108.     // hand it over.  Note that we don't need to give it the new  
  109.     // configuration, since we always send configuration changes to all  
  110.     // process when they happen so it can just use whatever configuration  
  111.     // it last got.  
  112.     if (r.app != null && r.app.thread != null) {  
  113.         try {  
  114.             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);  
  115.             r.app.thread.scheduleActivityConfigurationChanged(r.appToken);    //③调用ActivityThread.scheduleActivityConfigurationChanged();  
  116.         } catch (RemoteException e) {  
  117.             // If process died, whatever.  
  118.         }  
  119.     }  
  120.     r.stopFreezingScreenLocked(false);  
  121.   
  122.     return true;  
  123. }  

猜你喜欢

转载自blog.csdn.net/u010144805/article/details/80001041