概述
基于android 15.0, 以从强制横屏App上滑退回桌面流程来分析
frameworks/base/services/core/java/com/android/server/wm/AsyncRotationController.java
AsyncRotationController 是一种控制器,用于处理设备显示屏旋转时非活动窗口的异步更新。这种控制器通过异步处理来优化屏幕旋转或应用过渡动画的启动延迟,确保窗口在旋转过程中能够平滑过渡,避免闪烁或延迟问题。具体功能包括:
- 在旋转变化时处理窗口的淡出和淡入效果。
- 隐藏和显示目标窗口以匹配新的旋转角度。
- 使用同步事务管理无缝旋转,确保窗口能够平滑过渡到新的旋转状态。
Async Rotation执行时机
// DisplayContent.java
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
if (mFixedRotationLaunchingApp == null && r != null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
// 延迟隐藏动画,以避免在短时间内点击导航栏可能触发固定旋转时出现闪烁
final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents
|| mTransitionController.isTransientLaunch(r);
startAsyncRotation(shouldDebounce);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
// 如果请求display的下一次transition,保持异步旋转控制器。
if (!mTransitionController.hasCollectingRotationChange(this, getRotation())) {
finishAsyncRotationIfPossible();
}
}
mFixedRotationLaunchingApp = r;
}
用于启动异步旋转过程。这个过程允许应用程序或系统在不阻塞主线程的情况下处理显示屏的旋转,从而提供更平滑的用户体验.
// DisplayContent.java
private boolean startAsyncRotation(boolean shouldDebounce) {
if (shouldDebounce) {
mWmService.mH.postDelayed(() -> {
synchronized (mWmService.mGlobalLock) {
if (mFixedRotationLaunchingApp != null
&& startAsyncRotation(false /* shouldDebounce */)) {
// 应用该事务,使动画控制能够立即生效
getPendingTransaction().apply();
}
}
}, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS); //250ms
return false;
}
if (mAsyncRotationController == null) {
mAsyncRotationController = new AsyncRotationController(this);
mAsyncRotationController.start();
return true;
}
return false;
}
动画执行
- 收集目标窗口
// AsyncRotationController.java
AsyncRotationController(DisplayContent displayContent) {
.....
// 收集那些可以异步旋转而不阻塞display的窗口。
displayContent.forAllWindows(this, true /* traverseTopToBottom */);
......
}
public void accept(WindowState w) {
if (!w.mHasSurface || !canBeAsync(w.mToken)) {
return;
}
if (mTransitionOp == OP_LEGACY && w.mForceSeamlesslyRotate) {
// Legacy transition already handles seamlessly windows.
return;
}
......
// 大部分都是执行fade窗口动画
final int action = mTransitionOp == OP_CHANGE_MAY_SEAMLESS || w.mForceSeamlesslyRotate
? Operation.ACTION_SEAMLESS : Operation.ACTION_FADE;
mTargetWindowTokens.put(w.mToken, new Operation(action));
}
- 目标窗口在TO_FRONT transition启动时淡出
07-11 15:27:24.362 3750 3785 D AsyncRotation: Start fade-out Window{ae32994 u0 Floating XXX}
// AsyncRotationController.java
/**
* 为可能稍后无缝旋转的窗口令牌准备相应的操作(例如隐藏动画)
*/
void start() {
.....
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
final Operation op = mTargetWindowTokens.valueAt(i);
if (op.mAction == Operation.ACTION_FADE || op.mAction == Operation.ACTION_TOGGLE_IME) {
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
op.mLeash = windowToken.getAnimationLeash();
if (DEBUG) Slog.d(TAG, "Start fade-out " + windowToken.getTopChild());
} else if (op.mAction == Operation.ACTION_SEAMLESS) {
op.mLeash = windowToken.mSurfaceControl;
if (DEBUG) Slog.d(TAG, "Start seamless " + windowToken.getTopChild());
}
}
.....
}
- 目标窗口在CHANGE transition启动时以new rotation重绘
3750 3785 V WindowManager: Resize reasons for w=Window{eb45fff u0 Floating XXX}: forceReportingResized=false insetsChanged=true configChanged=true didFrameInsetsChange=true
3750 3785 I WindowManager: Resizing Window{ae32994 u0 Floating XXX} WITH DRAW PENDING
3750 3785 V WindowManager: Requested redraw for orientation change: Window{ae32994 u0 Floating XXX}
3750 7896 I WindowManager: finishDrawing of orientation change: Window{ae32994 u0 Floating XXX} 100ms
// WindowState.java
void updateResizingWindowIfNeeded() {
......
// display rotation改变, 所以这里的configChanged为true
final boolean configChanged = !mInRelayout && !isLastConfigReportedToClient();
......
final boolean contentChanged = didFrameInsetsChange || configChanged
|| dragResizingChanged || attachedFrameChanged;
.....
if (contentChanged || insetsChanged || shouldSendRedrawForSync()) {
ProtoLog.v(WM_DEBUG_RESIZE,
"Resize reasons for w=%s: %s configChanged=%b didFrameInsetsChange=%b",
this, mWindowFrames.getInsetsChangedInfo(),
configChanged, didFrameInsetsChange);
.....
// 重置当前窗口的mDrawState为DRAW_PENDING
if ((configChanged || getOrientationChanging() || dragResizingChanged)
&& isVisibleRequested()) {
winAnimator.mDrawState = DRAW_PENDING;
.....
}
if (!mWmService.mResizingWindows.contains(this)) {
ProtoLog.v(WM_DEBUG_RESIZE, "Resizing window %s", this);
mWmService.mResizingWindows.add(this);
}
} .....
}
void reportResized() {
......
ProtoLog.v(WM_DEBUG_RESIZE, "Reporting new frame to %s: %s", this,
mWindowFrames.mCompatFrame);
final boolean drawPending = mWinAnimator.mDrawState == DRAW_PENDING;
if (drawPending) {
ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this);
}
.....
final boolean reportDraw = syncRedraw || drawPending;
.....
if (Flags.bundleClientTransactionFlag()) {
getProcess().scheduleClientTransactionItem(
WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,
mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
alwaysConsumeSystemBars, displayId,
syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
mLastReportedActivityWindowInfo));
onResizePostDispatched(drawPending, prevRotation, displayId);
}
......
}
- TO_FRONT动画结束时开始淡入目标窗口
07-11 15:27:25.532 3750 7896 D AsyncRotation: handleFinishDrawing Window{ae32994 u0 Floating XXX}
07-11 15:27:25.532 3750 7896 D AsyncRotation: Complete set pending Window{ae32994 u0 Floating XXX}
07-11 15:27:25.572 3750 8951 D AsyncRotation: Setup unrotate Window{ae32994 u0 Floating XXX}
07-11 15:27:25.636 3750 7166 D AsyncRotation: Complete directly Window{ae32994 u0 Floating XXX}
07-11 15:27:25.636 3750 7166 D AsyncRotation: finishOp fade-in Window{ae32994 u0 Floating XXX}
// AsyncRotationController.java
void completeAll() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
finishOp(mTargetWindowTokens.keyAt(i));
}
mTargetWindowTokens.clear();
onAllCompleted();
}
private void finishOp(WindowToken windowToken) {
final Operation op = mTargetWindowTokens.remove(windowToken);
......
else if (op.mAction == Operation.ACTION_FADE) {
if (DEBUG) Slog.d(TAG, "finishOp fade-in " + windowToken.getTopChild());
// The previous animation leash will be dropped when preparing fade-in animation, so
// simply apply new animation without restoring the transformation.
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
} ......
}