本文主要是介绍Android13屏幕旋转的基本逻辑,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.问题
1.settings put system user_rotation 1是什么意思
答案:设置用户期望的屏幕转向,0代表:Surface.ROTATION_0竖屏;1代表:Surface.ROTATION_90横屏
2.设置user_rotation和GSensor哪个优先级更高,比如user_rotation = 0期待竖屏,但是打开屏幕旋转且处于横屏时,应该是横屏还是竖屏
答案:此时GSensor优先级更高,横屏显示,具体原因看第三个问题。
3.systemui中的“自动旋转”按钮影响的是哪个数据和系统的值
Settings.System.ACCELEROMETER_ROTATION和DisplayRotation.userRotationMode:
/** 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;USER_ROTATION_FREE :如果应用不指定屏幕方向,sensor传感器决定
USER_ROTATION_LOCKED:如果应用不指定屏幕方向,user决定方向,即user_rotation数据库值。
打开自动旋转时候设置的是Settings.System.ACCELEROMETER_ROTATION值会设置成1,代表,否则设置成0,这个值会直接影响DisplayRotation.userRotationMode的值:
final int userRotationMode = Settings.System.getIntForUser(resolver,Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) !=0? WindowManagerPolicy.USER_ROTATION_FREE: WindowManagerPolicy.USER_ROTATION_LOCKED;也就说如果打开了自动旋转,userRotationMode = USER_ROTATION_FREE,代表通过sensor决定;否则设置
成USER_ROTATION_LOCKED,由user_rotation决定
2.GSensor触发屏幕转向的流程分析
现在我们知道,屏幕方向旋转是通过加速度传感器或者重力传感器来获取,但Sensor上报的原始数据还得通过一系列计算得到最终的旋转角度,这部分逻辑在WindowOrientationListener.AccelSensorJudge
类中进行计算转换,当Sensor收到onSensorChanged()回调后,对原始数据进行加工,最终得到屏幕方向旋转角度,并当方向旋转值发生变化时,调用WindowOrientationListener#onProposedRotationChanged()方法发起更新:
public void onProposedRotationChanged(int rotation) {ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);// Send interaction power boost to improve redraw performance.mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);if (isRotationChoicePossible(mCurrentAppOrientation)) {final boolean isValid = isValidRotationChoice(rotation);sendProposedRotationChangeToStatusBarInternal(rotation, isValid);} else {mService.updateRotation(false /* alwaysSendConfiguration */,false /* forceRelayout */);}}
mService.updateRotation->wms.updateRotationUnchecked->displayContent.updateRotationUnchecked->DisplayRotation.updateRotationUnchecked如下:
/*** Update rotation with an option to force the update. This updates the container's perception* of rotation and, depending on the top activities, will freeze the screen or start seamless* rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}* during {@link DisplayContent#sendNewConfiguration}.** @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating* orientation because we're waiting for some rotation to finish or display* to unfreeze, which results in configuration of the previously visible* activity being applied to a newly visible one. Forcing the rotation* update allows to workaround this issue.* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL* {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE* THE SCREEN.*/boolean updateRotationUnchecked(boolean forceUpdate) {final int displayId = mDisplayContent.getDisplayId();if (!forceUpdate) {if (mDeferredRotationPauseCount > 0) {// Rotation updates have been paused temporarily. Defer the update until updates// have been resumed.ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");return false;}final ScreenRotationAnimation screenRotationAnimation =mDisplayContent.getRotationAnimation();if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {// Rotation updates cannot be performed while the previous rotation change animation// is still in progress. Skip this update. We will try updating again after the// animation is finished and the display is unfrozen.ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");return false;}if (mService.mDisplayFrozen) {// Even if the screen rotation animation has finished (e.g. isAnimating returns// false), there is still some time where we haven't yet unfrozen the display. We// also need to abort rotation here.ProtoLog.v(WM_DEBUG_ORIENTATION,"Deferring rotation, still finishing previous rotation");return false;}if (mDisplayContent.mFixedRotationTransitionListener.shouldDeferRotation()) {// Makes sure that after the transition is finished, updateOrientation() can see// the difference from the latest orientation source.mLastOrientation = SCREEN_ORIENTATION_UNSET;// During the recents animation, the closing app might still be considered on top.// In order to ignore its requested orientation to avoid a sensor led rotation (e.g// user rotating the device while the recents animation is running), we ignore// rotation update while the animation is running.return false;}}if (!mService.mDisplayEnabled) {// No point choosing a rotation if the display is not enabled.ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");return false;}//当前(未更新)的屏幕转向final int oldRotation = mRotation;//最近一次应用请求的转向final int lastOrientation = mLastOrientation;//根据给定的显示方向确定一个屏幕方向final int rotation = rotationForOrientation(lastOrientation, oldRotation);ProtoLog.v(WM_DEBUG_ORIENTATION,"Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "+ "oldRotation=%s (%d)",Surface.rotationToString(rotation), rotation,displayId,ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,Surface.rotationToString(oldRotation), oldRotation);ProtoLog.v(WM_DEBUG_ORIENTATION,"Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId,ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,Surface.rotationToString(rotation), rotation);if (oldRotation == rotation) {// No change.return false;}// Preemptively cancel the running recents animation -- SysUI can't currently handle this// case properly since the signals it receives all happen post-change. We do this earlier// in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems// to happen too late.final RecentsAnimationController recentsAnimationController =mService.getRecentsAnimationController();if (recentsAnimationController != null) {recentsAnimationController.cancelAnimationForDisplayChange();}ProtoLog.v(WM_DEBUG_ORIENTATION,"Display id=%d rotation changed to %d from %d, lastOrientation=%d",displayId, rotation, oldRotation, lastOrientation);if (deltaRotation(oldRotation, rotation) != Surface.ROTATION_180) {mDisplayContent.mWaitingForConfig = true;}mRotation = rotation;mDisplayContent.setLayoutNeeded();if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();final TransitionRequestInfo.DisplayChange change = wasCollecting ? null: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),oldRotation, mRotation);mDisplayContent.requestChangeTransitionIfNeeded(ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);if (wasCollecting) {// Use remote-rotation infra since the transition has already been requested// TODO(shell-transitions): Remove this once lifecycle management can cover all// rotation cases.startRemoteRotation(oldRotation, mRotation);}return true;}mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {// The screen rotation animation uses a screenshot to freeze the screen while windows// resize underneath. When we are rotating seamlessly, we allow the elements to// transition to their rotated state independently and without a freeze required.prepareSeamlessRotation();} else {prepareNormalRotationAnimation();}// Give a remote handler (system ui) some time to reposition things.startRemoteRotation(oldRotation, mRotation);return true;}
其中特别重要的函数时rotationForOrientation,根据当下屏幕方向和应用请求的方向,决定最终的屏幕显示方向,看下其实现方式:
int rotationForOrientation(@ScreenOrientation int orientation,@Surface.Rotation int lastRotation) {ProtoLog.v(WM_DEBUG_ORIENTATION,"rotationForOrientation(orient=%s (%d), last=%s (%d)); user=%s (%d) %s",ActivityInfo.screenOrientationToString(orientation), orientation,Surface.rotationToString(lastRotation), lastRotation,Surface.rotationToString(mUserRotation), mUserRotation,mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED? "USER_ROTATION_LOCKED" : "");//用户锁定了方向,直接返回user_rotation数据设置的用户期待的值if (isFixedToUserRotation()) {return mUserRotation;}//获取Sensor检测到的屏幕方向值int sensorRotation = mOrientationListener != null? mOrientationListener.getProposedRotation() // may be -1: -1;mLastSensorRotation = sensorRotation;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) {// For secondary displays we ignore things like displays sensors, docking mode and// rotation lock, and always prefer user rotation.preferredRotation = mUserRotation;} else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {// Ignore sensor when lid switch is open and rotation is forced.preferredRotation = mLidOpenRotation;} else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR&& (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {// Ignore sensor when in car dock unless explicitly enabled.// This case can override the behavior of NOSENSOR, and can also// enable 180 degree rotation while docked.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)) {// Ignore sensor when in desk dock unless explicitly enabled.// This case can override the behavior of NOSENSOR, and can also// enable 180 degree rotation while docked.preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;} else if (hdmiPlugged && mDemoHdmiRotationLock) {// Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.// Note that the dock orientation overrides the HDMI orientation.preferredRotation = mDemoHdmiRotation;} else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED&& mUndockedHdmiRotation >= 0) {// Ignore sensor when plugged into HDMI and an undocked orientation has// been specified in the configuration (only for legacy devices without// full multi-display support).// Note that the dock orientation overrides the HDMI orientation.preferredRotation = mUndockedHdmiRotation;} else if (mDemoRotationLock) {// Ignore sensor when demo rotation lock is enabled.// Note that the dock orientation and HDMI rotation lock override this.preferredRotation = mDemoRotation;} else if (mDisplayPolicy.isPersistentVrModeEnabled()) {// While in VR, apps always prefer a portrait rotation. This does not change// any apps that explicitly set landscape, but does cause sensors be ignored,// and ignored any orientation lock that the user has set (this conditional// should remain above the ORIENTATION_LOCKED conditional below).preferredRotation = mPortraitRotation;} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {// Application just wants to remain locked in the last rotation.preferredRotation = lastRotation;} else if (!mSupportAutoRotation) {// If we don't support auto-rotation then bail out here and ignore// the sensor and any rotation lock settings.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) {// Otherwise, use sensor only if requested by the application or enabled// by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.if (sensorRotation != Surface.ROTATION_180|| getAllowAllRotations() == 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) {// Apply rotation lock. Does not apply to NOSENSOR or specific rotations.// The idea is that the user rotation expresses a weak preference for the direction// of gravity and as NOSENSOR is never affected by gravity, then neither should// NOSENSOR be affected by rotation lock (although it will be affected by docks).// Also avoid setting user rotation when app has preference over one particular rotation// to avoid leaving the rotation to the reverse of it which has the compatible// orientation, but isn't what app wants, when the user rotation is the reverse of the// preferred rotation.preferredRotation = mUserRotation;} else {// No overriding preference.// We will do exactly what the application asked us to do.preferredRotation = -1;}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;}}
参考文章:
安卓开发之屏幕旋转_安卓开发 屏幕旋转逻辑-CSDN博客
https://juejin.cn/post/6958440883276496927
https://juejin.cn/post/6982153383880687624
这篇关于Android13屏幕旋转的基本逻辑的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!