Android rotation 转屏过程分析

http://blog.csdn.net/yunnywucy/article/details/40045839


转屏的代码逻辑主要在WMS(窗口管理)这一块。

整个转屏过程可以分为以下几个步骤:

1。计算出屏幕的下一个rotation;

2。window 重新布局计算

3。


下面是调用的流程图



  一开始当然是Sensor 从驱动传上来值,然后上层来判断要不要转屏, 这部分代码位WindowOrientationListener, WindowOrientationListener是一个抽象类。在他的内部有一个SensorEventListenerImpl 实现了SensorEventListener 用于监听Sensor

[java]  view plain copy
  1. private WindowOrientationListener(Context context, Handler handler, int rate) {    
  2.       mHandler = handler;    
  3.       mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);    
  4.       mRate = rate;    
  5.       mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR    
  6.               ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);    
  7.       if (mSensor != null) {    
  8.           // Create listener only if sensors do exist    
  9.           mSensorEventListener = new SensorEventListenerImpl();    
  10.       }    
  11.   }    


[java]  view plain copy
  1. private WindowOrientationListener(Context context, Handler handler, int rate) {  
  2.       mHandler = handler;  
  3.       mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);  
  4.       mRate = rate;  
  5.       mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR  
  6.               ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);  
  7.       if (mSensor != null) {  
  8.           // Create listener only if sensors do exist  
  9.           mSensorEventListener = new SensorEventListenerImpl();  
  10.       }  
  11.   }  
其中USE_GRAVITY_SENSOR 等于false ,也就是说目前我们手机里转屏用的Sensor 是加速度传感器,并非重力感应器

mSensorEventListener 的 registerListener 和 unregisterListener 分别位于WindowOrientationListener的enable()  和 disable ()方法中。

[java]  view plain copy
  1.  1public void enable() {    
  2.  2.            synchronized (mLock) {    
  3.  3.            if (mSensor == null) {    
  4.  4.                Log.w(TAG, "Cannot detect sensors. Not enabled");    
  5.  5.                return;    
  6.  6.            }    
  7.  7.            if (mEnabled == false) {    
  8.  8.                if (LOG) {    
  9.  9.                    Log.d(TAG, "WindowOrientationListener enabled");    
  10. 10.                }    
  11. 11.                mSensorEventListener.resetLocked();    
  12. 12.                mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);    
  13. 13.                mEnabled = true;    
  14. 14.            }    
  15. 15.        }    
  16. 16.    }    
  17. 17.     
  18. 18.    /**  
  19. 19.     * Disables the WindowOrientationListener.  
  20. 20.     */    
  21. 21.    public void disable() {    
  22. 22.     
  23. 23.        synchronized (mLock) {    
  24. 24.            if (mSensor == null) {    
  25. 25.                Log.w(TAG, "Cannot detect sensors. Invalid disable");    
  26. 26.                return;    
  27. 27.            }    
  28. 28.            if (mEnabled == true) {    
  29. 29.                if (LOG) {    
  30. 30.                    Log.d(TAG, "WindowOrientationListener disabled");    
  31. 31.                }    
  32. 32.                mSensorManager.unregisterListener(mSensorEventListener);    
  33. 33.                mEnabled = false;    
  34. 34.            }    
  35. 35.        }    
  36. 36.    }    


[java]  view plain copy
  1. public void enable() {  
  2.            synchronized (mLock) {  
  3.            if (mSensor == null) {  
  4.                Log.w(TAG, "Cannot detect sensors. Not enabled");  
  5.                return;  
  6.            }  
  7.            if (mEnabled == false) {  
  8.                if (LOG) {  
  9.                    Log.d(TAG, "WindowOrientationListener enabled");  
  10.                }  
  11.                mSensorEventListener.resetLocked();  
  12.                mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);  
  13.                mEnabled = true;  
  14.            }  
  15.        }  
  16.    }  
  17.   
  18.    /** 
  19.     * Disables the WindowOrientationListener. 
  20.     */  
  21.    public void disable() {  
  22.   
  23.        synchronized (mLock) {  
  24.            if (mSensor == null) {  
  25.                Log.w(TAG, "Cannot detect sensors. Invalid disable");  
  26.                return;  
  27.            }  
  28.            if (mEnabled == true) {  
  29.                if (LOG) {  
  30.                    Log.d(TAG, "WindowOrientationListener disabled");  
  31.                }  
  32.                mSensorManager.unregisterListener(mSensorEventListener);  
  33.                mEnabled = false;  
  34.            }  
  35.        }  
  36.    }  
WindowOrientationListener 的enable() 和disbale() 的调用位于PhoneWindowManager的updateOrientationListenerLp 方法中。该方法会在screen on, screen off 和Settings数据库发生变化的时候调用。

  下面我们回到SensorEventListenerImpl,我们知道当sensor 值发生变化时会调用onSensorChanged。在这里经过一些算法比较后。确定是不是要转屏。这部分代码我就没仔细看了,有兴趣可以自己研究。总之,再判断要转屏之后。会调用WindowOrientationListener的onProposedRotationChanged方法。

这个方法是一个抽象的方法。他的实现位于MyOrientationListener中, MyOrientationListener是PhoneWindowManager 里面的内部类。onProposedRotationChanged就是简单调用updateRotation, 然后调用WindowManagerService的updateRotation方法, 再到updateRotationUnchecked方法

  我们来看updateRotationUnchecked方法

      

[java]  view plain copy
  1. <strong xmlns="http://www.w3.org/1999/xhtml">   1public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {    
  2.    2.         if(DEBUG_ORIENTATION) Slog.v(TAG, "updateRotationUnchecked("    
  3.    3.                    + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")");    
  4.    4.     
  5.    5.         long origId = Binder.clearCallingIdentity();    
  6.    6.         boolean changed;    
  7.    7.         synchronized(mWindowMap) {    
  8.    8.             changed = updateRotationUncheckedLocked(false);    
  9.    9.             if (!changed || forceRelayout) {    
  10.   10.                 getDefaultDisplayContentLocked().layoutNeeded = true;    
  11.   11.                 performLayoutAndPlaceSurfacesLocked();    
  12.   12.             }    
  13.   13.         }    
  14.   14.     
  15.   15.         if (changed || alwaysSendConfiguration) {    
  16.   16.             sendNewConfiguration();    
  17.   17.         }    
  18.   18.     
  19.   19.         Binder.restoreCallingIdentity(origId);    
  20.   20.     }  </strong>  


        我们可以看到updateRotationUnchecked主要做了3件事:

   1。更新rotation 

    2。通知到AMS Configuration changed

        首先,我们来分析updateRotationUncheckedLocked, 

        

[java]  view plain copy
  1. 1int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);     


[java]  view plain copy
  1. int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);   

        updateRotationUncheckedLocked第一个很重要的方法,就是mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);  mForcedAppOrientation一般情况是由当前Resume状态的Activity决定的。具体实现是在PhoneWindowManager 中,作用就是根据mForcedAppOrientation和 rotation计算出新的rotation


       

[java]  view plain copy
  1. mRotation = rotation;  
  2. mAltOrientation = altOrientation;  
  3. mPolicy.setRotationLw(mRotation);  
  4.   
  5. mWindowsFreezingScreen = true;  
  6. mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);  
  7. mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);  
  8. mWaitingForConfig = true;  
  9. final DisplayContent displayContent = getDefaultDisplayContentLocked();  
  10. displayContent.layoutNeeded = true;  
  11. final int[] anim = new int[2];  
  12. if (displayContent.isDimming()) {  
  13.     anim[0] = anim[1] = 0;  
  14. else {  
  15.     mPolicy.selectRotationAnimationLw(anim);  
  16. }  
  17. startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);  
  18. // startFreezingDisplayLocked can reset the ScreenRotationAnimation.  
  19. screenRotationAnimation =  
  20.         mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);  

        上面的代码主要就是设置rotation,  开始freezing screen 。  设置 freezing_time_out , 获取转屏动画等操作。

         我们再往下看:

 

[java]  view plain copy
  1. final WindowList windows = displayContent.getWindowList();  
  2. for (int i = windows.size() - 1; i >= 0; i--) {  
  3.     WindowState w = windows.get(i);  
  4.     if (w.mHasSurface) {  
  5.         if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);  
  6.         w.mOrientationChanging = true;  
  7.         mInnerFields.mOrientationChangeComplete = false;  
  8.     }  
  9.     w.mLastFreezeDuration = 0;  
  10. }  

         这里设置了两个重要的属性:

        

[java]  view plain copy
  1. w.mOrientationChanging = true;  
  2.   
  3. lt;pre name="code" class="java">mInnerFields.mOrientationChangeComplete = false;  

 

        注意这两个属性和转屏结束有关。

        接着  如果changed true 表示,rotation 要发生变化。  则调用sendNewConfiguration  。其作用就是调用AMS 的  mActivityManager.updateConfiguration(null);

[java]  view plain copy
  1.    void sendNewConfiguration() {  
  2.         try {  
  3.             mActivityManager.updateConfiguration(null);  
  4.         } catch (RemoteException e) {  
  5.         }  
  6.     }  
       AMS 的 updateConfiguration 方法 首先会调用 WMS的 computeNewConfiguration 来计算出新的config.  其最终实现是在WMS的computeScreenConfigurationLocked中。 有兴趣可以具体分析。

      接着AMS会根据得到的新的config 调用 updateConfigurationLocked(values, null, false, false);       这个方法是系统更新config 的方法。任何config changed都会调用到。做过theme 的应该都了解。 updateConfigurationLocked其主要作用就是更新App的configuration  发送CONFIGURATION_CHANGED 广播 。在这个方法最后会调用到WMS 

  

[java]  view plain copy
  1. if (values != null && mWindowManager != null) {  
  2.          mWindowManager.setNewConfiguration(mConfiguration);  
  3.   }  
  代码如下  
[java]  view plain copy
  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.   
  8.     synchronized(mWindowMap) {  
  9.         mCurConfiguration = new Configuration(config);  
  10.         if (mWaitingForConfig) {  
  11.             mWaitingForConfig = false;  
  12.             mLastFinishedFreezeSource = "new-config";  
  13.         }  
  14.         performLayoutAndPlaceSurfacesLocked();  
  15.     }  
  16. }  

  会更新WMS 这边mCurConfiguration。 然后调用 performLayoutAndPlaceSurfacesLocked 对窗口重新布局绘制

   在performLayoutAndPlaceSurfacesLocked 执行过程中会对每个可见的window 调用updateResizingWindows(w); 我们来看updateResizingWindows


[java]  view plain copy
  1. private void updateResizingWindows(final WindowState w) {  
  2.        final WindowStateAnimator winAnimator = w.mWinAnimator;  
  3.        if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq) {  
  4.            w.setInsetsChanged();  
  5.            boolean configChanged = w.isConfigChanged();  
  6.            w.mLastFrame.set(w.mFrame);  
  7.            if (w.mContentInsetsChanged  
  8.                    || w.mVisibleInsetsChanged  
  9.                    || winAnimator.mSurfaceResized  
  10.                    || configChanged) {  
  11.                             
  12.                w.mLastOverscanInsets.set(w.mOverscanInsets);  
  13.                w.mLastContentInsets.set(w.mContentInsets);  
  14.                w.mLastVisibleInsets.set(w.mVisibleInsets);  
  15.                makeWindowFreezingScreenIfNeededLocked(w);  
  16.                // If the orientation is changing, then we need to  
  17.                // hold off on unfreezing the display until this  
  18.                // window has been redrawn; to do that, we need  
  19.                // to go through the process of getting informed  
  20.                // by the application when it has finished drawing.  
  21.                if (w.mOrientationChanging) {  
  22.                    if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG,  
  23.                            "Orientation start waiting for draw mDrawState=DRAW_PENDING in "  
  24.                            + w + ", surface " + winAnimator.mSurfaceControl);  
  25.                    winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;  
  26.                    if (w.mAppToken != null) {  
  27.                        w.mAppToken.allDrawn = false;  
  28.                        w.mAppToken.deferClearAllDrawn = false;  
  29.                    }  
  30.                }  
  31.                if (!mResizingWindows.contains(w)) {  
  32.                    if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,  
  33.                            "Resizing window " + w + " to " + winAnimator.mSurfaceW  
  34.                            + "x" + winAnimator.mSurfaceH);  
  35.                    mResizingWindows.add(w);  
  36.                }  
  37.            } else if (w.mOrientationChanging) {  
  38.                if (w.isDrawnLw()) {  
  39.                    if (DEBUG_ORIENTATION) Slog.v(TAG,  
  40.                            "Orientation not waiting for draw in "  
  41.                            + w + ", surface " + winAnimator.mSurfaceControl);  
  42.                    w.mOrientationChanging = false;  
  43.                    w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()  
  44.                            - mDisplayFreezeTime);  
  45.                }  
  46.            }  
  47.        }  
  48.    }  
  由于WMS的 mConfiguration 已经改变了。而windowstate.isConfigChaned 会返回true

       

[java]  view plain copy
  1. boolean isConfigChanged() {  
  2.     boolean configChanged = mConfiguration != mService.mCurConfiguration  
  3.             && (mConfiguration == null  
  4.                     || (mConfiguration.diff(mService.mCurConfiguration) != 0));  
  5.   
  6.     if (mAttrs.type == TYPE_KEYGUARD) {  
  7.         // Retain configuration changed status until resetConfiguration called.  
  8.         mConfigHasChanged |= configChanged;  
  9.         configChanged = mConfigHasChanged;  
  10.     }  
  11.   
  12.     return configChanged;  
  13. }  

         可以看到当转屏时 configChanged 发生了变化。 需要对window 重新布局和绘制。并且会加入   mResizingWindows.add(w);如果

[java]  view plain copy
  1. if (w.mContentInsetsChanged  
  2.                     || w.mVisibleInsetsChanged  
  3.                     || winAnimator.mSurfaceResized  
  4.                     || configChanged) {  

都没有发生。且 w.mOrientationChanging 为true 且 w.isDrawnLw() ,已经绘制完成,则会将w.mOrientationChanging 设为false

              performLayoutAndPlaceSurfacesLocked 接下来会对 mResizingWindows 里面的所有窗口调用 resize 处理。 最终会调用到应用端,让应用重新布局绘制。


              performLayoutAndPlaceSurfacesLocked 的最后会调用scheduleAnimationLocked 来播放动画 
动画的循环执行是在WindowAnimator animateLocked 方法中。 这个方法在每次开始的时候。都会将

      

[java]  view plain copy
  1. mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;  
             在这个方法里面还调用每个窗口的windowStateAnimator的 prepareSurfaceLocked方法

[java]  view plain copy
  1.       final WindowList windows = mService.getWindowListLocked(displayId);  
  2.                 final int N = windows.size();  
  3.                 for (int j = 0; j < N; j++) {  
  4.                     windows.get(j).mWinAnimator.prepareSurfaceLocked(true);  
  5.       }  




[java]  view plain copy
  1. final WindowList windows = mService.getWindowListLocked(displayId);  
  2.                 final int N = windows.size();  
  3.                 for (int j = 0; j < N; j++) {  
  4.                     windows.get(j).mWinAnimator.prepareSurfaceLocked(true);  
  5.                 }  


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值