Android 屏幕旋转流程分析

本文详细解析Android屏幕旋转的源头设置、AndroidManifest中Activity标签、setRequestedOrientation方法的应用,以及特殊需求的处理,揭示系统如何响应屏幕旋转指令。

一、概述

        Android 系统的屏幕能旋转的前提是设备需要具有sensor硬件设备,sensor实时将设备的旋转数据上报给上层,上层对上包上来的数据进行处理,并且改变屏幕的坐标和方向,最后呈现在我们面前的就是屏幕的正常旋转。所以下面将从Android Setting中自动旋转开关、AndroidManifest中Activity指定screenOrientation标签、通过Activity的setRequestedOrientation方法设置以及底层sensor上报到上层屏幕旋转四个流程分析屏幕的旋转,并且会在第六步分析常见的需求。

二、Android Setting中自动旋转开关流程

        Android 原生Settings中的辅助功能中有很多关于显示的接口,比如三指放大等等,而屏幕旋转的开关也在其中。具体就是AccessibilitySettings.java(packages/apps/Settings//src/com/android/settings/accessibility/AccessibilitySettings.java)中,整体的流程图如下图2-1所示,下面从Setting模块开始分析自动旋转开关流程。

在这里插入图片描述

图2-1 屏幕自动旋转开关流程
 //packages/apps/Settings//src/com/android/settings/accessibility/AccessibilitySettings.java
 @Override                                                                               
 public boolean onPreferenceTreeClick(Preference preference) {
   
                              
     if (mToggleHighTextContrastPreference == preference) {
   
                                 
         handleToggleTextContrastPreferenceClick();                                      
         return true;                                                                    
     ....  
     } else if (mToggleLockScreenRotationPreference == preference) {
   
                        
         handleLockScreenRotationPreferenceClick();                                      
         return true;                                                                    
     ....                                    
     }                                                                                   
     return super.onPreferenceTreeClick(preference);                                     
 }     
//最后Settings是调用RotationPolicy中的相关方法进一步处理
private void handleLockScreenRotationPreferenceClick() {
   
             
    RotationPolicy.setRotationLockForAccessibility(getActivity(), 
            !mToggleLockScreenRotationPreference.isChecked());    
}                                                                 

        Settings中实现很简单最后调用到 RotationPolicy.java(frameworks/base/core/java/com/android/internal/view/RotationPolicy.java)中的setRotationLockForAccessibility方法实现屏幕是否自动旋转。

//frameworks/base/core/java/com/android/internal/view/RotationPolicy.java 
public static final int NATURAL_ROTATION = getNaturalRotation();
 //通过ro.primary_display.user_rotation属性来获取默认的屏幕方向
private static int getNaturalRotation() {
   
                                              
     int rotation = SystemProperties.getInt("ro.primary_display.user_rotation", 0);  
     switch (rotation) {
   
                                                                
         case 90:                                                                    
             return Surface.ROTATION_90;                                             
         case 180:                                                                   
             return Surface.ROTATION_180;                                            
         case 270:                                                                   
             return Surface.ROTATION_270;                                            
         default:                                                                    
             break;                                                                  
     }                                                                               
     return Surface.ROTATION_0;                                                      
 }                                                                                   
public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
   
      
     Settings.System.putIntForUser(context.getContentResolver(),                                
             Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,      
                     UserHandle.USER_CURRENT);                                                  
     //最后调用setRotationLock来实现,NATURAL_ROTATION为默认的屏幕方向                                                                              
     setRotationLock(enabled, NATURAL_ROTATION);                                                
 }                                                                                              

        该方法中会设置Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY字段:

  • 0 表示自动旋转屏幕打开,屏幕解冻,可以自动旋转
  • 1 表示自动旋转屏幕关闭,屏幕固定锁死

可以通过下面手动adb 命令打开和关闭屏幕旋转功能

adb shell settings put system hide_rotation_lock_toggle_for_accessibility 0

        我们继续回到setRotationLockForAccessibility方法中,最后调用setRotationLock这个接口继续执行,注意此方法的第二个参数是默认的屏幕方向,是通过surfaceFlinger在初始化时候的设置的ro.primary_display.user_rotation这个属性获得。

//frameworks/base/core/java/com/android/internal/view/RotationPolicy.java 
private static void setRotationLock(final boolean enabled, final int rotation) {
   
           
     new AsyncTask<Boolean, Void, Void>() {
   
                                                 
         @Override                                                                       
         protected Void doInBackground(Boolean... params){
   
                                  
             try {
   
                                                                          
                 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 
                 //这里的params[0]就是传入的第一个参数enable,如果为true,就是冻结屏幕,否则解冻 	
                 if (params[0]) {
   
                                                           
                     wm.freezeRotation(rotation);                                        
                 } else {
   
                                                                   
                     wm.thawRotation();                                                  
                 }                                                                       
             } catch (RemoteException exc) {
   
                                                
                 Log.w(TAG, "Unable to save auto-rotate setting");                       
             }                                                                           
             return null;                                                                
         }                                                                               
     }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, enabled);                       
 }                                                                                       

        IWindowManager的实现类为WMS(WindowMangerService),最后调用到WMS中的freezeRotation方法进行冻结屏幕,防止屏幕自动旋转,调用thawRotation方法解冻屏幕,注意此时freezeRotation方法传入屏幕旋转的方向,作为固定的屏幕的方向,例如 冻结前是竖屏,冻结后就是竖屏,反之为横屏。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void freezeRotation(int rotation) {
   
                                                
    freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);                          
}   

@Override                                                                              
public void freezeDisplayRotation(int displayId, int rotation) {
   
                          
    // TODO(multi-display): Track which display is rotated.                            
    ....                                     
    try {
   
                                                                                 
        synchronized (mGlobalLock) {
   
                                                      
            //1: 调用DisplayContent的getDisplayRotation()获取到DisplayRotation
            //然后调用DisplayRotation的freezeRotation方法设置当前用户选择的屏幕方向
            display.getDisplayRotation().freezeRotation(rotation);                     
        }                                                                              
    } finally {
   
                                                                           
        Binder.restoreCallingIdentity(origId);                                         
    }                                                                           
    //2: 判断屏幕是否旋转,通知Config发生变化,如果布局改变,需要重新绘制布局                               
    updateRotationUnchecked(false, false);                                             
}


//解冻屏幕的方式类似
@Override                                                                             
public void thawRotation() {
   
                                                             
    thawDisplayRotation(Display.DEFAULT_DISPLAY);                                     
}                                                                                     
                                                                                      
/**                                                                                   
 * Thaw rotation changes.  (Disable "rotation lock".)                                 
 * Persists across reboots.                                                           
 */                                                                                   
@Override                                                                             
public void thawDisplayRotation(int displayId) {
   
                                         
    ...                                                                                                           
    long origId = Binder.clearCallingIdentity();                                      
    try {
   
                                                                                
        synchronized (mGlobalLock) {
   
                                                     
            ...
            //1: 调用DisplayRotation的thawRotation()方法解冻屏幕
            display.getDisplayRotation().thawRotation();
        }                                                                             
    } finally {
   
                                                                          
        Binder.restoreCallingIdentity(origId);                                        
    }                                                                                 
    //2:判断屏幕是否旋转,通知Config发生变化,如果布局改变,需要重新绘制布局                              
    updateRotationUnchecked(false, false);                                            
}                                                                                                                             

        WMS中屏幕锁定和屏幕自动旋转两个实现很相似,主要是调用DisplayRotation的freezeRotation方法锁定用户指定的屏幕方向,调用thawRotation方法,解锁用户固定屏幕,恢复屏幕自动旋转。最后调用updateRotationUnchecked,发送新的Configuration变化,以及如果布局发生变化,也会重新计算布局。

//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java 
void freezeRotation(int rotation) {
   
                                           
    rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation;
    setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);   
}                                                                          
                                                                    
void thawRotation() {
   
                                                         
    setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
}                                                                          

        DisplayRotation中最后都是调用setUserRotation方法处理,传入的参数不通,第一个参数为用户选择的选择模式(自动旋转或者屏幕锁定);第二个参数为用户选择的屏幕方向。

//frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
/** When not otherwise specified by the activity's screenOrientation, rotation should be 
 * determined by the system (that is, using sensors). */                                 
public final int USER_ROTATION_FREE = 0;    
/** When not otherwise specified by the activity's screenOrientation, rotation is set by 
 * the user. */                                                                          
public final int USER_ROTATION_LOCKED = 1;                                                                               
  • WindowManagerPolicy.USER_ROTATION_LOCKED: 屏幕的旋转将和系统的sensor保持一致。值得注意的是,这个生效的前提是activity没有设置screenOrientation属性。
  • WindowManagerPolicy.USER_ROTATION_FREE :屏幕的旋转是由用户选择而定,前提也是需要activity没有设置screenOrientation属性。
//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java 
private void setUserRotation(int userRotationMode, int userRotation) {
   
                    
    //1: 默认屏幕显示,通过settings数据库的监听,不需要更新内部的值。
    if (isDefaultDisplay) {
   
                                                                       
    // We'll be notified via settings listener, so we don't need to update internal values.
    final ContentResolver res = mContext.getContentResolver();                             
    final int accelerometerRotation =                                                      
            userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;          
    Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,             
            accelerometerRotation, UserHandle.USER_CURRENT);                               
    Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,        
            UserHandle.USER_CURRENT);                                                      
    return;                                            
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GitFranc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值