android10屏幕旋转流程
1.手机怎么检测到屏幕方向发生变化
手机会打开一个OrientationListener回调,当sensor检测到屏幕方向发生变化后会回调到windowManagerService
2.检测到屏幕屏幕方向发生变化后的流程
当确认屏幕发生旋转则调用旋转流程先截屏然后旋转然后relayout。
屏幕方向发生旋转是根据sensor回调来通知系统。
整体流程如下图,调用链还是比较复杂的
当从systemUI或者Settings中打开自动转屏开关后,DisplayRotation的内部类SettignsObserver会监听到,然后会打开sensor监听屏幕旋转。
看一下调用栈:
DisplayRotation.SettingsObserver.onChange()->updateSettings()->updateOritationListenerLw()->OritaionListener.enable()
这个一个典型的观察者模式,有兴趣同学可以了解一下观察者模式。
直接看OritaionListener代码
private class OrientationListener extends WindowOrientationListener {
final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
boolean mEnabled;
OrientationListener(Context context, Handler handler) {
super(context, handler);
}
private class UpdateRunnable implements Runnable {
final int mRotation;
UpdateRunnable(int rotation) {
mRotation = rotation;
}
@Override
public void run() {
// Send interaction hint to improve redraw performance.
mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
if (isRotationChoicePossible(mCurrentAppOrientation)) {
final boolean isValid = isValidRotationChoice(mRotation);
sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
} else {
//这里发生屏幕旋转通知windowManagerService
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
}
}
}
@Override
public void onProposedRotationChanged(int rotation) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
Runnable r = mRunnableCache.get(rotation, null);
if (r == null) {
r = new UpdateRunnable(rotation);
mRunnableCache.put(rotation, r);
}
getHandler().post(r);
}
//打开sensor监听
@Override
public void enable(boolean clearCurrentRotation) {
super.enable(clearCurrentRotation);
mEnabled = true;
if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
}
//关闭sensor监听
@Override
public void disable() {
super.disable();
mEnabled = false;
if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
}
}
如果enable listener当发生屏幕旋转事件后会回调到DisplayRoation,然后回到WindowManagerService准备旋转屏幕
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
windowManagerService中的updateRotaion这里开始屏幕旋转流程。
看一下updateRotation
/**
* Recalculate the current rotation.
*
* Called by the window manager policy whenever the state of the system changes
* such that the current rotation might need to be updated, such as when the
* device is docked or rotated into a new posture.
*/
@Override
public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
...
try {
synchronized (mGlobalLock) {
boolean layoutNeeded = false;
final int displayCount = mRoot.mChildren.size();
for (int i = 0; i < displayCount; ++i) {
final DisplayContent displayContent = mRoot.mChildren.get(i);
//调用displayContent 继续判断是否需要旋转屏幕
final boolean rotationChanged = displayContent.updateRotationUnchecked();
if (!rotationChanged || forceRelayout) {
displayContent.setLayoutNeeded();
layoutNeeded = true;
}
if (rotationChanged || alwaysSendConfiguration) {
displayContent.sendNewConfiguration();
}
}
if (layoutNeeded) {
mWindowPlacerLocked.performSurfacePlacement();
}
}
} finally {
...
}
}
DisplayContent:
boolean updateRotationUnchecked(boolean forceUpdate) {
.............
//这里省略了很多代码,其实这里有很重要的一些步骤,都是在判断当前屏幕是否需要旋转,
//由于我们这次讨论的重点在于屏幕旋转的流程,就不在细看其他流程。
//关于android如何判断屏幕发生了旋转,后面会写一篇博客来说明
//开始冻结屏幕
mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
// startFreezingDisplayLocked can reset the ScreenRotationAnimation.
return true;
}
这里如果检测需要旋转屏幕,则先冻结屏幕,然后拿到冻结的这一帧做一个旋转动画
WindowManagerService:
//冻结屏幕
void startFreezingDisplayLocked(int exitAnim, int enterAnim,
DisplayContent displayContent) {
//这里创建screenRotationAnimation
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
}
}
创建转屏动画的时候对当前屏幕截屏,拿到surfaceFlinger一帧
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
//拿到一帧
SurfaceControl.ScreenshotGraphicBuffer gb =
mService.mDisplayManagerInternal.screenshot(displayId);
..............
}
接下来就是对这一帧做旋转动画
然后看一下旋转动画,在WindowManagerService中
if (layoutNeeded) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"updateRotation: performSurfacePlacement");
mWindowPlacerLocked.performSurfacePlacement();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
这里会发消息给handler,收到消息后会调用到PerformSurfacePlacement
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
mPerformSurfacePlacement = () -> {
synchronized (mService.mGlobalLock) {
performSurfacePlacement();
}
};
}
final void performSurfacePlacement(boolean force) {
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
...........
经过一系列调用,最后来到stopFreezingScreen
public void stopFreezingScreen() {
synchronized (mGlobalLock) {
if (mClientFreezingScreen) {
try {
//停止屏幕冻结
stopFreezingDisplayLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
void stopFreezingDisplayLocked() {
if (!mDisplayFrozen) {
return;
}
//调用dismiss
if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
..........
}
dismiss流程
public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
//开始屏幕旋转动画
if (!mStarted) {
startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
true, exitAnim, enterAnim);
}
}
开始转屏动画
private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, boolean dismissing,
int exitAnim, int enterAnim) {
if (mSurfaceControl == null) {
// Can't do animation.
return false;
}
if (mStarted) {
return true;
}
mStarted = true;
boolean firstStart = false;
// Figure out how the screen has moved from the original rotation.
int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
if (TWO_PHASE_ANIMATION && mFinishExitAnimation == null
&& (!dismissing || delta != Surface.ROTATION_0)) {
if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
firstStart = true;
mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_start_exit);
}
最后发送 config消息,通知activity ReLaunch
at com.android.server.wm.ActivityRecord.relaunchActivityLocked(ActivityRecord.java:3584)
at com.android.server.wm.ActivityRecord.ensureActivityConfiguration(ActivityRecord.java:3462)
at com.android.server.wm.ActivityRecord.ensureActivityConfiguration(ActivityRecord.java:3280)
at com.android.server.wm.ActivityTaskManagerService.ensureConfigAndVisibilityAfterUpdate(ActivityTaskManagerService.java:5971)
at com.android.server.wm.ActivityTaskManagerService.updateDisplayOverrideConfigurationLocked(ActivityTaskManagerService.java:5494)
at com.android.server.wm.ActivityTaskManagerService.updateDisplayOverrideConfiguration(ActivityTaskManagerService.java:4599)
at com.android.server.wm.WindowManagerService.sendNewConfiguration(WindowManagerService.java:4632)
总结一下:
1.sensor检测到屏幕方向发生变化,回调到wms
2.wms判断当前需要旋转屏幕,则截屏,dismiss,旋转
3.屏幕旋转完成,relayout window ,relaunch activity