一、概述
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模块开始分析自动旋转开关流程。

//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;

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

被折叠的 条评论
为什么被折叠?



