本文基于AndroidS源码分析
Orientation和Rotation的区别
orientation标识此时界面以什么方向显示,可以理解为是横屏还是竖屏
rotation旋转方向是指界面相对于默认情况顺时针旋转的角度(平板一般默认横屏,而小屏幕设备默认竖屏)
orientation
//core/java/android/content/res/Configuration.java
@IntDef(prefix = {"ORIENTATION_"}, value = {
ORIENTATION_UNDEFINED,
ORIENTATION_PORTRAIT,
ORIENTATION_LANDSCAPE,
ORIENTATION_SQUARE
})
public static final int ORIENTATION_UNDEFINED = 0;
public static final int ORIENTATION_PORTRAIT = 1;
public static final int ORIENTATION_LANDSCAPE = 2;
@Deprecated public static final int ORIENTATION_SQUARE = 3;
// core/java/android/content/pm/ActivityInfo.java
@IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
SCREEN_ORIENTATION_UNSET,
SCREEN_ORIENTATION_UNSPECIFIED,
SCREEN_ORIENTATION_LANDSCAPE,
SCREEN_ORIENTATION_PORTRAIT,
SCREEN_ORIENTATION_USER,
SCREEN_ORIENTATION_BEHIND,
SCREEN_ORIENTATION_SENSOR,
SCREEN_ORIENTATION_NOSENSOR,
SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
SCREEN_ORIENTATION_SENSOR_PORTRAIT,
SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
SCREEN_ORIENTATION_REVERSE_PORTRAIT,
SCREEN_ORIENTATION_FULL_SENSOR,
SCREEN_ORIENTATION_USER_LANDSCAPE,
SCREEN_ORIENTATION_USER_PORTRAIT,
SCREEN_ORIENTATION_FULL_USER,
SCREEN_ORIENTATION_LOCKED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScreenOrientation {}
ActivityInfo中的screenOrientation用于记录app强制设定的方向或旋转模式,
而Configuration.java中的orientation值可以认为只有横屏或竖屏,
当app具体渲染时候只需要区分横屏竖屏即可,不必在意具体旋转方向。
rotation
// core/java/android/view/Surface.java
@IntDef(prefix = { "ROTATION_" }, value = {
ROTATION_0,
ROTATION_90,
ROTATION_180,
ROTATION_270
})
@Retention(RetentionPolicy.SOURCE)
public @interface Rotation {}
public static final int ROTATION_0 = 0;
public static final int ROTATION_90 = 1;
public static final int ROTATION_180 = 2;
public static final int ROTATION_270 = 3;
旋转流程:从Sensor到onConfigurationChanged
旋转锁定开关
系统的旋转锁定开关变化后会修改SettingsProvider数据,触发DisplayRotation的ContentObserver监听
// services/core/java/com/android/server/wm/DisplayRotation.java
private boolean updateSettings() {
final ContentResolver resolver = mContext.getContentResolver();
boolean shouldUpdateRotation = false;
synchronized (mLock) {
boolean shouldUpdateOrientationListener = false;
....
// 省略的这段代码读取系统旋转相关设置确定是否需要启用OrientationListener监听sensor
if (shouldUpdateOrientationListener) {
// updateOrientationListenerLw方法回去真正注册sensor监听
updateOrientationListenerLw(); // Enable or disable the orientation listener.
}
...
}
return shouldUpdateRotation;
}
发生屏幕旋转
发生屏幕旋转时,WindowOrientationListener的onSensorChanged会处理接收到的sensor event并计算出新的proposedRotation,进入DisplayRotation$OrientationListener$UpdateRunnable
的run方法。
// services/core/java/com/android/server/wm/DisplayRotation.java
public void run() {
// Send interaction power boost to improve redraw performance.
mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
if (isRotationChoicePossible(mCurrentAppOrientation)) {
final boolean isValid = isValidRotationChoice(mRotation);
sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
} else {
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
}
}
关键方法:WMS#updateRotationUnchecked
- 方法首先调用displayContent#updateRotationUncheckedLocked()
->mDisplayRotation.updateRotationUnchecked进行rotation的更新。
- 在这个方法中将通知container对于rotation的感知然后根据顶部的activity开始冻屏动画或者无缝动画
如果该方法返回值为true则必须调用DisplayContent#sendNewConfiguration完成屏幕旋转或解冻
- 除此之外,updateRotationUnchecked方法还会调用
rotation = rotationForOrientation(lastOrientation, oldRotation)
来计算新的rotation。
- rotationForOrientation是一个很长的方法,这个方法非常重要:前面的if-else主要是结合当前系统显示模式、旋转模式以及sensor监测到的旋转方向给出一个preferredRotation,后面的switch-case则是根据当前的orientation决定是否可以使用preferredRotation。
/**
* 给定一个方向常数,将传感器、DOCK模式、旋转锁定和其他因素考虑在内,返回适当的表面旋转。
*/
@VisibleForTesting
@Surface.Rotation
int rotationForOrientation(@ScreenOrientation int orientation,
@Surface.Rotation int lastRotation) {
...
// 方向锁定
if (isFixedToUserRotation()) {
return mUserRotation;
}
// 传感器方向未确定
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
if (sensorRotation < 0) {
sensorRotation = lastRotation;
}
final int lidState = mDisplayPolicy.getLidState();
final int dockMode = mDisplayPolicy.getDockMode();
final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
final boolean carDockEnablesAccelerometer =
mDisplayPolicy.isCarDockEnablesAccelerometer();
final boolean deskDockEnablesAccelerometer =
mDisplayPolicy.isDeskDockEnablesAccelerometer();
final int preferredRotation;
if (!isDefaultDisplay) {
// 对于外接显示,我们忽略了像显示传感器、对接模式和旋转锁定
preferredRotation = mUserRotation;
} else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
// 翻盖开关打开时,强制旋转时忽略传感器。
preferredRotation = mLidOpenRotation;
} else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
&& (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
// 车载模式如果明确指定启用传感器使用传感器方向
preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
} else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
|| dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
|| dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
&& (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
// 在deskdock模式除非直接启用否则忽略sensor
preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
} else if ((hdmiPlugged || WFDRegisterStub.getWifiDisplayConnected()) && mDemoHdmiRotationLock) {
// 插入HDMI或无线头屏时,如果HDMI旋转锁定启用,则使用这个rotation
preferredRotation = mDemoHdmiRotation;
} else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
&& mUndockedHdmiRotation >= 0) {
// 插入HDMI并且在configuratin里指定了undocked orientation时忽略sensor
preferredRotation = mUndockedHdmiRotation;
} else if (mDemoRotationLock) {
// 启用demo旋转锁时用mDemoRotation方向
preferredRotation = mDemoRotation;
} else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
// VR时使用竖屏方向
preferredRotation = mPortraitRotation;
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
// 应用锁定方向
preferredRotation = lastRotation;
} else if (!mSupportAutoRotation) {
// 不支持自动旋转 这里直接退出
preferredRotation = -1;
} else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
&& (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
|| orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|| orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|| orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
// 否则,仅在应用程序请求或默认启用USER或未指定模式时才使用传感器。
if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
// 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(
R.bool.config_allowAllRotations)
? ALLOW_ALL_ROTATIONS_ENABLED
: ALLOW_ALL_ROTATIONS_DISABLED;
}
if (sensorRotation != Surface.ROTATION_180
|| mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
preferredRotation = sensorRotation;
} else {
preferredRotation = lastRotation;
}
} else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
&& orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
&& orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
&& orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
&& orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
&& orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
// 应用旋转锁。不适用于NOSENSOR或一些特定的旋转。
preferredRotation = mUserRotation;
} else {
// No overriding preference.
// We will do exactly what the application asked us to do.
preferredRotation = WFDRegisterStub.selectPreferredRotation();
}
// 下面的逻辑:根据当前的orientation决定是否可以使用preferredRotation
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:
case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
// Return either landscape rotation.
if (isLandscapeOrSeascape(preferredRotation)) {
return preferredRotation;
}
if (isLandscapeOrSeascape(lastRotation)) {
return lastRotation;
}
return mLandscapeRotation;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
case ActivityInfo.SCREEN_ORIENTATION_USER_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;
}
}
configuration更新:WMS->ATMS->Activity
前面的流程主要是用来更新DisplayRotation.mRotation,如果rotation有更新,则sendNewConfiguration()通知DisplayContent更新configuration。configuration更新流程由DisplayContent#sendNewConfiguration开始,调用到ActivityTaskManagerService,最终通过ActivityRecord将config组装成ActivityConfigurationChangeItem通知到AppThread端:
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
ActivityConfigurationChangeItem.obtain(config));
configuration更新流程的源码本文先不做解析,上时序图: