2.3 窗口状态刷新
当应用端执行measure-layout-draw之后,便会调用WindowManagerService.finishDrawingWindow,处理Surface的状态变更并将Surface show出来。 首先还是看一下该阶段的流程图,对整个流程有个初步的了解。 将整个流程分为三部分:
1.WMS接受客户端请求,将mDrawState更新为COMMIT_DRAW_PENDING,并请求窗口布局。
2.mDrawState更新为HAS_DRAW,再次请求窗口布局。
3.执行show Surface。
2.3.1 接受客户端请求
代码路径:framework/services/core/java/com/android/server/wm/Session.java
@Override
public void finishDrawing(IWindow window,
@Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
//调用WMS中的finishDrawingWindow处理
mService.finishDrawingWindow(this, window, postDrawTransaction, seqId);
}
2.3.2 finishDrawingWindow
1.在WMS中根据客户端的Binder在mWindowMap中获取对应的WindowState。
2.调用WindowState.finishDrawing执行mDrawState的状态变更。
3.将WindowState.mLayoutNeeded标志位置为true。
4.请求进行布局刷新。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
void finishDrawingWindow(Session session, IWindow client,
@Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
if (postDrawTransaction != null) {
postDrawTransaction.sanitize();
}
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
/*1.根据客户端的Binder在mWindowMap中获取对应的WindowState*/
WindowState win = windowForClientLocked(session, client, false);
ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
/*2.finishDrawing执行mDrawState的状态更变*/
if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
if (win.hasWallpaper()) {
win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
/*3.将DisplayContent中mLayoutNeeded置为true*/
//该标志位是判断是否进行窗口大小尺寸计算的条件之一
win.setDisplayLayoutNeeded();
/*4.请求进行布局刷新*/
mWindowPlacerLocked.requestTraversal();
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
1.mDrawState的状态更变
在finishDrawingWindow中调用WindowState的finishDrawing方法
win.finishDrawing(postDrawTransaction, seqId)
这个方法主要调用了WindowStateAnimator的finishDrawingLocked进行状态更变
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction, int syncSeqId) {
......
//调用WindowStateAnimator.finishDrawingLocked,会将mDrawState的状态更改为COMMIT_DRAW_PENDING
final boolean layoutNeeded =
mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
mClientWasDrawingForSync = false;
// We always want to force a traversal after a finish draw for blast sync.
return !skipLayout && (hasSyncHandlers || layoutNeeded);
}
我们继续看看WindowStateAnimator中的finishDrawingLocked()方法 首先判断mDrawState的状态是否为DRAW_PENDING,在我们创建SurfaceControl时,会将mDrawState状态更新为DRAW_PENDING。因此接下来将状态调整为COMMIT_DRAW_PENDING。 代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
boolean forceApplyNow) {
......
boolean layoutNeeded = false;
if (mDrawState == DRAW_PENDING) {
......
//如果当前状态为DRAW_PENDING,则将mDrawState更变为COMMIT_DRAW_PENDING
mDrawState = COMMIT_DRAW_PENDING;
layoutNeeded = true;
}
......
return layoutNeeded;
}
2.请求布局刷新
在finishDrawingWindow中请求布局刷新
mWindowPlacerLocked.requestTraversal();
requestTraversal中主要做了两件事:
1.首先将遍历标志为mTraversalSchedule置为true。
2.其次发送handle消息mPerformSurfacePlacement
void requestTraversal() {
//判断遍历标志mTraversalScheduled是否为true
if (mTraversalScheduled) {
return;
}
// Set as scheduled even the request will be deferred because mDeferredRequests is also
// increased, then the end of deferring will perform the request.
//将遍历标志位置为true
mTraversalScheduled = true;
if (mDeferDepth > 0) {
mDeferredRequests++;
if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
return;
}
//发送handle消息,处理消息会调用mPerformSurfacePlacement
mService.mAnimationHandler.post(mPerformSurfacePlacement);
}
mPerformSurfacePlacement会新建一个线程调用performSurfacePlacement。 performSurfacePlacement方法我们在讲relayoutWindow相关流程的时候讲过,这是执行遍历布局的入口。可以回看下,在Android T WMS窗口添加流程其三——服务端代码详解(窗口位置和大小计算)中的【2.2.4 计算窗口大小位置中的“1.处理窗口布局循环”】
private class Traverser implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
//调用执行performSurfacePlacement
performSurfacePlacement();
}
}
}
private final Traverser mPerformSurfacePlacement = new Traverser();
final void performSurfacePlacement(boolean force) {
//当mDeferDepth大于0且force为false时,则将延迟布局请求数+1,并直接返回
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
//将循环的最大次数设置为6次
int loopCount = 6;
do {
//将该标志为设置为false
mTraversalScheduled = false;
//执行窗口布局操作
performSurfacePlacementLoop();
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
//只有当mTraversalScheduled为true且循环次数大于0时,才会再次循环执行布局
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
private void performSurfacePlacementLoop() {
//若当前已经进行布局操作,则无需重复调用直接返回
if (mInLayout) {
......
return;
}
......
//将该标志位置为true,表示正在处于布局过程中
mInLayout = true;
......
try {
/*1.调用RootWindowContainer的performSurfacePlacement()方法对所有窗口执行布局操作*/
mService.mRoot.performSurfacePlacement();
mInLayout = false;
//判断每个(DisplayContent)屏幕的mLayoutNeeded标志位是否为true
if (mService.mRoot.isLayoutNeeded()) {
/*2.若需要布局,且布局次数小于6次,则需要再次请求布局*/
if (++mLayoutRepeatCount < 6) {
//该方法中会将mTraversalScheduled标志位设置位true
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
}
} catch (RuntimeException e) {
mInLayout = false;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
}
代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
//调用performSurfacePlacementNoTrace()
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
// "Something has changed! Let's make it correct now."
// TODO: Super long method that should be broken down...
void performSurfacePlacementNoTrace() {
......
/*1.如果有焦点变化,更新焦点*/
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
......
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
//开启事务,获取GlobalTransactionWrapper对象
mWmService.openSurfaceTransaction();
try {
/*2.执行窗口尺寸计算,surface状态变更等操作*/
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
//关闭事务,把事务提交
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
......
/*3.将Surface状态变更为HAS_DRAWN,触发App触发动画。该过程在“2.3.3mDrawState变更为HAS_DRAW”流程中再详细分析*/
checkAppTransitionReady(surfacePlacer);
......
/*4.遍历所有DisplayContent,如果壁纸有变化,更新壁纸*/
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
//判断DisplayContent的壁纸是否需要改变
if (displayContent.mWallpaperMayChange) {
ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change! Adjusting");
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
displayContent.pendingLayoutChanges);
}
}
}
/*5.在此处理焦点变化*/
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
......
/*6.如果过程中size或者位置变化,则通知客户端重新relayout*/
handleResizingWindows();
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
mOrientationChangeComplete);
}
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
mWmService.stopFreezingDisplayLocked();
}
// Destroy the surface of any windows that are no longer visible.
/*7.销毁不可见的窗口*/
i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
......
}
这里我们主要关注applySurfaceChangesTransaction();和checkAppTransitionReady(surfacePlacer);
* 窗口位置计算与窗口状态刷新流程不同点
可以发现,窗口位置计算流程与窗口状态刷新流程都调用了performSurfacePlacement,两次调用的主要不同点在于:
1.窗口状态刷新流程在DisplayContent.applySurfaceChangesTransaction中调用mApplySurfaceChangesTransaction,处理mDrawState状态。
2.窗口状态刷新流程在RootWindowContainer.performSurfacePlacementNoTrace中调用checkAppTransitionReady,处理mDrawState状态变更为HAS_DRAWN,触发Activity过渡动画。
3.窗口状态刷新流程在WindowSurfacePlacementLoop.performSurfacePlacementLoop中会调用requestTraversal,请求再次布局。
4.窗口状态刷新流程在mDrawState状态变更为HAS_DRAWN后,会通过WindowStateAnimator.prepareSurfaceLocked处理surface的位置、大小以及显示等。
2.3.3 mDrawState变更为HAS_DRAW
1.mApplySurfaceChangesTransaction
RootWindowContainer的applySurfaceChangesTransaction()方法最终会调用到DisplayContent中调用的applySurfaceChangesTransaction()方法(在Android T WMS窗口添加流程其三——服务端代码详解(窗口位置和大小计算)中的【2.2.4 计算窗口大小位置】中讲过流程,不再赘述)
我们接着该方法中的mApplySurfaceChangesTransaction跟踪。
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
如果当前WindowState存在surfaceControl,则进入到WindowStateAnimator进行mDrawState的状态更变。
代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
......
//首先判断当前windowState的是否有surfaceControl
if (w.mHasSurface) {
// Take care of the window being ready to display.
//调用WindowStateAnimator的commitFinishDrawingLocked()方法
final boolean committed = winAnimator.commitFinishDrawingLocked();
......
}
......
};
继续看看WindowStateAnimator的commitFinishDrawingLocked()方法
final boolean committed = winAnimator.commitFinishDrawingLocked();
1.对mDrawState的状态进行过滤,非COMMIT_DRAW_PENDING和READY_TO_SHOW则直接返回。
2.此时我们的mDrawState已经在“【2.3.2 finishDrawingWindow】”将状态更新为COMMIT_DRAW_PENDING,因此此处将其变更为READY_TO_SHOW。
代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
// This must be called while inside a transaction.
boolean commitFinishDrawingLocked() {
//非COMMIT_DRAW_PENDING和READY_TO_SHOW则直接返回
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
mSurfaceController);
//将状态更变为READY_TO_SHOW
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
//直接进入到WindowState.performShowLocked()流程的三种情况
//1.如果ActivityRecord为空,这种情况可以理解为不依赖Activity的窗口,比如常见的悬浮窗
//2.或者canShowWindows()为true,这个方法大概是说:只有当所有窗口都已绘制完成,并且没有正在进行父级窗口的应用过渡动画,并且没有非默认颜色的窗口存在时,返回true
//3.或者窗口类型为启动窗口,启动窗口就是StartingWindow,应用启动时出现的窗口,常见的就是Splash screen ,许多应用都会定义自己的SplashActivity
//进入performShowLocked()流程后mDrawState更新HAS_DRAWN
//由于非这三种情况最终也会调用到performShowLocked(),因此下面这种情况我们暂不讨论
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
}
2.checkAppTransitionReady()
这里我们继续跟踪RootWindowContainer.performSurfacePlacementNoTrace()方法中的checkAppTransitionReady()方法
checkAppTransitionReady(surfacePlacer);
该方法会遍历所有DisplayContent,处理activity的过滤动画,此处我们只有跟踪有关mDrawState状态更变的相关代码
代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
// Trace all displays app transition by Z-order for pending layout change.
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent curDisplay = mChildren.get(i);
// If we are ready to perform an app transition, check through all of the app tokens
// to be shown and see if they are ready to go.
//检查所有要显示的app token,是否已经准备就绪
if (curDisplay.mAppTransition.isReady()) {
// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
curDisplay.mAppTransitionController.handleAppTransitionReady();
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
curDisplay.pendingLayoutChanges);
}
}
......
}
}
调用AppTransitionController的handleAppTransitionReady()方法,该方法主要做了以下事情
1.处理activity的过渡动画(远程动画)
2.分别调用 handleClosingApps以及handleOpeningApps对要关闭的和要打开的Activity进行可见性更新。
3.调用AppTransition.goodToGo方法走播放远程动画流程。
4.调用onTransitionStarting方法开始快照相关流程。详细流程见:TaskSnapshot创建和移除流程
5.由于activity的可见性变更,将DisplayContent.mLayoutNeeded设置为true,该标志位在DisplayContent.performLayoutNoTrace中用来判断是否对当前DisplayContent下的所有窗口进行刷新。
代码路径:framework/services/core/java/com/android/server/wm/AppTransitionController.java
/**
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
......
//通过getTransitCompatType方法获取transit的值
@TransitionOldType final int transit = getTransitCompatType(
mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
......
//获取正在打开 (mOpeningApps)、关闭 (mClosingApps) 和切换 (mChangingContainers) 的应用的activity类型
//并将它们存储在 activityTypes 集合中。
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
//被用于查找与给定transit和activityTypes相关的 ActivityRecord
//也就是我们当前打开的应用的ActivityRecord
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
mDisplayContent.mChangingContainers);
//获取正在打开的应用列表 (mOpeningApps) 中的顶层应用。
//ignoreHidden 参数设置为 false,意味着即使应用是隐藏的,也会被考虑在内
final ActivityRecord topOpeningApp =
getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
//获取正在关闭的应用列表 (mClosingApps) 中的顶层应用
final ActivityRecord topClosingApp =
getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
//获取正在切换的应用列表 (mChangingContainers) 中的顶层应用
//其取决于参数DisplayContent.mChangingContainers中是否有值
/**
有三种情况会给DisplayContent.mChangingContainers中添加值
1.{@link Task}在全屏和浮窗之间发生切换
2.{@link TaskFragment}已组织好并且正在更改窗口边界
3.{@link ActivityRecord}被重新分配到一个有组织的{@link TaskFragment}中
**/
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
//从之前找到的animLpActivity(正在打开的应用的ActivityRecord)的窗口中获取布局参数
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
......
try {
/*1.1应用app transition动画(远程动画)*/
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
animLp, voiceInteraction);
/*1.2.1处理closing activity可见性*/
handleClosingApps();
/*1.2.2处理opening actvity可见性*/
handleOpeningApps();
//处理用于处理正在切换的应用
handleChangingApps(transit);
//处理正在关闭或更改的容器
handleClosingChangingContainers();
//设置与最后一次应用过渡动画相关的信息
appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp, topChangingApp);
final int flags = appTransition.getTransitFlags();
/*1.3播放远程动画*/
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
//处理非应用窗口的过渡动画
handleNonAppWindowsInTransition(transit, flags);
//执行动画回调
appTransition.postAnimationCallback()
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
/*1.4 开始快照相关流程*/
mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
mDisplayContent.mClosingChangingContainers.clear();
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
/*2.由于activity的可见性变更,将DisplayContent.mLayoutNeeded标志位置为true*/
mDisplayContent.setLayoutNeeded();
......
}
下面我们来讲下这个方法里面的主要方法
applyAnimations()
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction);
基于一组ActivityRecord来应用动画,这些ActivityRecord表示正在进行切换的应用。
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps这两个参数分别代表正在打开和关闭的应用;
transit通过前面getTransitCompatType方法中获取,是TRANSIT_OLD_WALLPAPER_CLOSE(12);
animLp通过前面getAnimLp方法中获取,用于定义窗口的布局参数。这里就是代表正在打开的应用的ActivityRecord的窗口布局参数;
voiceInteraction:表示是否为语音交互。
/**
* Apply an app transition animation based on a set of {@link ActivityRecord}
*
* @param openingApps The list of opening apps to which an app transition animation applies.
* @param closingApps The list of closing apps to which an app transition animation applies.
* @param transit The current transition type.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
LayoutParams animLp, boolean voiceInteraction) {
//方法检查过渡类型是否未设置,或者打开和关闭的应用程序是否都为空。如果是,则方法直接返回,不执行任何动画。
if (transit == WindowManager.TRANSIT_OLD_UNSET
|| (openingApps.isEmpty() && closingApps.isEmpty())) {
return;
}
//调用getAnimationTargets方法获取打开和关闭的窗口容器(WindowContainer)
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
//打开和关闭的窗口应用动画。这是通过调重载的applyAnimations方法完成的,传递相应的参数,如动画的目标、过渡类型等。
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
//如果存在最近任务动画控制器(RecentsAnimationController),则发送任务出现任务
final RecentsAnimationController rac = mService.getRecentsAnimationController();
if (rac != null) {
rac.sendTasksAppeared();
}
//遍历打开和关闭的应用,并设置mOverrideTaskTransition为false
for (int i = 0; i < openingApps.size(); ++i) {
openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
}
for (int i = 0; i < closingApps.size(); ++i) {
closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
}
//如果存在辅助功能控制器(AccessibilityController)且有回调,则调用其onAppWindowTransition方法。
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController.hasCallbacks()) {
accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
传递关键参数,处理应用程序窗口的打开和关闭动画。
通过getAnimationTargets方法获取当前打开和关闭的应用的容器,即ActivityRecord的容器。
最关键的方法是调用的applyAnimations方法:
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
我们这里openingWcs和closingWcs实际上表示的是应用的容器,即Task;openingApps 和 closingApps就是前面传递的mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,分别代表正在打开和关闭的应用,也是挂在对应Task下面的ActivityRecord。并且传递了应用的可见性visible,true可见,false不可见。
比如,在我们桌面点击打开应用的流程中,openingWcs实际上指的是应用的Task,openingApps是应用的ActivityRecord(其实就是应用的主界面),其可见性为true;closingWcs对应的是桌面的Task,closingApps是桌面的ActivityRecord,其可见性为false。
注: 从这里开始后续流程执行了两次,第一次是打开的应用流程,第二次是关闭的应用流程(一个应用的启动,伴随这另一个应用的退出,浮窗等特殊场景除外)。 从桌面点击开启应用的场景来说,一次是启动的应用角度执行流程,另一次是桌面角度执行流程。 从代码逻辑上来说,唯一的不同点是传递的可见性的值不同。
这是调用的是重载的applyAnimations方法
/**
* Apply animation to the set of window containers.
*
* @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
* @param apps The list of {@link ActivityRecord}s being transitioning.
* @param transit The current transition type.
* @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
* invisible.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
@TransitionOldType int transit, boolean visible, LayoutParams animLp,
boolean voiceInteraction) {
//获取窗口容器的数量
final int wcsCount = wcs.size();
//遍历每一个应用的窗口容器
for (int i = 0; i < wcsCount; i++) {
final WindowContainer wc = wcs.valueAt(i);
// If app transition animation target is promoted to higher level, SurfaceAnimator
// triggers WC#onAnimationFinished only on the promoted target. So we need to take care
// of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
// app transition.
//对于每一个应用的窗口容器,检查正在进行切换的应用(apps)中哪些是该窗口容器的后代。
//就比如应用的ActivityRecord是是应用的Task的后代
final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
for (int j = 0; j < apps.size(); ++j) {
final ActivityRecord app = apps.valueAt(j);
//app如果是wc的后代,将其添加到一个列表中。
if (app.isDescendantOf(wc)) {
transitioningDescendants.add(app);
}
}
//调用每个应用的窗口容器的applyAnimation方法,传入相应的参数
//这些参数包含动画的布局、过渡类型、是否可见、是否有语音交互以及需要做动画的ActivityRecord应用的列表。
wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
入参含义:
wcs: 一个WindowContainer对象的集合,这些对象是需要应用动画的窗口容器。
apps: 一个ActivityRecord对象的集合,这些对象表示正在进行切换的应用程序。
transit: 当前的过渡类型,例如淡入淡出、滑动等。
visible: 一个布尔值,表示应用是否变为可见。
animLp: 布局参数,定义了动画运行时的布局。
voiceInteraction: 一个布尔值,表示是否有语音交互。
关键代码解读:
-
final WindowContainer wc = wcs.valueAt(i);获取窗口容器wcs是前面传递过来的是Task,wc就是依次获取当前应用的Task。 比如,在我们桌面点击打开应用的流程中,就是获取了应用的Task和桌面的Task。 -
transitioningDescendants存储的就是需要做动画的ActivityRecord。 -
传递动画参数 通过
wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);方法,传递参数动画的布局、过渡类型、是否可见、是否有语音交互以及需要做动画的ActivityRecord应用的列表。wc就是Task,其没有applyAnimation方法,向上找父类WindowContainer.applyAnimation方法调用。
这部分远程动画流程,这里不做重点,详细流程见 远程动画流程
handleClosingApps() 该方法中主要的作用就是将所有即将close的activity的mVisible标志设置为false。该标志位在后续prepareSurfaces中是判断是否show surface的条件之一。
private void handleClosingApps() {
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final int appsCount = closingApps.size();
for (int i = 0; i < appsCount; i++) {
final ActivityRecord app = closingApps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);
//设置activity的可见性,将mVisible设置为false
app.commitVisibility(false /* visible */, false /* performLayout */);
app.updateReportedVisibilityLocked();
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
// gotten drawn.
//强制将allDrawn设置为true
app.allDrawn = true;
......
}
}
handleOpeningApps() 该方法与handleClosingApps方法类似,主要处理两件事情:
1.将所有即将open的activity的mVisible标志位设置为true.
2.调用ActivityRecord.showAllWindowsLocked(),最终会调用到WindowState.performShowLocked() ,处理mDrawState的状态变更
private void handleOpeningApps() {
final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
final int appsCount = openingApps.size();
for (int i = 0; i < appsCount; i++) {
final ActivityRecord app = openingApps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
/*1.设置activity的可见性,将mVisible设置为true*/
app.commitVisibility(true /* visible */, false /* performLayout */);
......
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION handleAppTransitionReady()");
//开启事务
mService.openSurfaceTransaction();
try {
/*2.此方法最终会调用到WindowState.performShowLocked*/
app.showAllWindowsLocked();
} finally {
//关闭事务
mService.closeSurfaceTransaction("handleAppTransitionReady");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION handleAppTransitionReady()");
}
......
}
}
app.showAllWindowsLocked();先调用到ActivityRecord的showAllWindowsLocked()
代码路径:framework/services/core/java/com/android/server/wm/ActivityRecord.java
/**
* This must be called while inside a transaction.
*/
void showAllWindowsLocked() {
forAllWindows(windowState -> {
if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState);
windowState.performShowLocked();
}, false /* traverseTopToBottom */);
}
windowState.performShowLocked();再调用到WindowState的performShowLocked()
将mDrawState的状态由READY_TO_SHOW变更为HAS_DRAW
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
// This must be called while inside a transaction.
boolean performShowLocked() {
......
//获取WindowStateAnimator.mDrawState
final int drawState = mWinAnimator.mDrawState;
//这里判断(drawState 状态为HAS_DRAWN 或者READY_TO_SHOW)且ActivityRecord不为空
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
//窗口类型不为启动窗口
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
mActivityRecord.onFirstWindowDrawn(this);
} else {
mActivityRecord.onStartingWindowDrawn();
}
}
//如果当前mDrawState的状态不为READY_TO_SHOW ,则直接返回
if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
return false;
}
......
//走入窗口动画流程
mWinAnimator.applyEnterAnimationLocked();
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
//设置mDrawState的状态为HAS_DRAWN
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
......
return true;
}
添加窗口时,会调用这个mWinAnimator.applyEnterAnimationLocked();窗口动画的方法,然后调用到applyAnimationLocked();窗口移除时,会直接调用applyAnimationLocked()显示动画。
后续动画相关流程可参考 本地动画流程
TaskSnapshot快照流程
mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
见Android T TaskSnapshot创建和移除流程
3.再次请求布局
回到WindowSurfacePlacer中通过requestTraversal(),再次请求布局,该方法将mTraversalScheduled标志位设置为true的判断条件有两个:
1.遍历所有DisplayContent.mLayoutNeeded标志为是否为true。(由于AppTransitionController.handleAppTransitionReady阶段已经将mLayoutNeeded置为true,因此该条件为真)
2.重复布局的次数不能超过6次,该条件也为真。(因为当前还只是第一次布局)
代码路径:framework/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
private void performSurfacePlacementLoop() {
......
try {
......
/*1.遍历所有DisplayContent.mLayoutNeeded标志位是否为true*/
if (mService.mRoot.isLayoutNeeded()) {
/*2.如果需要布局,且布局次数小于6次,则需要再次请求布局*/
if (++mLayoutRepeatCount < 6) {
//该方法中会将mTraversalScheduled标志位设置位true
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
......
} catch (RuntimeException e) {
......
}
}
接下来进入第二次布局循环,其主要目的是为了show surface
2.3.4 show Surface
在第二次循环中,我们主要关注DisplayContent中applySurfaceChangesTransaction()方法调用的prepareSurfaces()
该方法最终会调用到根容器WindowContainer,来遍历所有子容器中的prepareSurfaces。 代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java
@Override
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
//获取事务
final Transaction transaction = getPendingTransaction();
//调用其父类方法
super.prepareSurfaces();
// TODO: Once we totally eliminate global transaction we will pass transaction in here
// rather than merging to global.
SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
调用其父类方法super.prepareSurfaces();
DisplayContent的父类为WindowContainer
代码路径:framework/services/core/java/com/android/server/wm/WindowContainer.java
void prepareSurfaces() {
// If a leash has been set when the transaction was committed, then the leash reparent has
// been committed.
mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
//调用所有子容器中的prepareSurfaces
for (int i = 0; i < mChildren.size(); i++) {
mChildren.get(i).prepareSurfaces();
}
}
mChildren.get(i).prepareSurfaces();在WindowState.prepareSurfaces中,主要做了两方面工作。
1.将mWindowFrames中计算出来的left以及top设置surface位置,并调整窗口比例。
2.控制surface的可见性,查看WindowStateAnimator.prepareSurfaceLocked
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
void prepareSurfaces() {
mIsDimming = false;
applyDims();
//实际调用的是其父类WindowContainer的方法
/*1.最终调用自身的updateSurfacePosition()(自身有重写该方法)计算surface的位置*/
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
//更新窗口比例
updateScaleIfNeeded();
/*2.控制surface的可见性,调用WindowStateAnimator的prepareSurfaceLocked()方法*/
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
super.prepareSurfaces();
}
@Override
@VisibleForTesting
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
return;
}
//这段代码首先检查布局是否被延迟(通过 isLayoutDeferred() 方法)
//或者应用是否正在进行布局(通过 isGoneForLayout() 方法)。
//如果满足这些条件并且 mSurfacePlacementNeeded 为 false,则方法返回,不执行后续操作。
//这是因为当布局被延迟或应用正在进行布局时,界面的位置可能不是最新的,因此不执行updateSurfacePosition。
if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout())
&& !mSurfacePlacementNeeded) {
// Since this relies on mWindowFrames, changes made while layout is deferred are
// likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be
// up-to-date and thus can't be relied on.
return;
}
//将mSurfacePlacementNeeded设置为false
mSurfacePlacementNeeded = false;
//将mSurfacePosition的left以及top设置mWindowFrames中计算出来的left以及top,并根据parent进行偏移
transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
mSurfacePosition);
//根据壁纸的比例对SurfacePosition进行调整
if (mWallpaperScale != 1f) {
final Rect bounds = getLastReportedBounds();
Matrix matrix = mTmpMatrix;
matrix.setTranslate(mXOffset, mYOffset);
matrix.postScale(mWallpaperScale, mWallpaperScale, bounds.exactCenterX(),
bounds.exactCenterY());
matrix.getValues(mTmpMatrixArray);
mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
} else {
mSurfacePosition.offset(mXOffset, mYOffset);
}
......
}
mWinAnimator.prepareSurfaceLocked(getSyncTransaction()); 调用WindowStateAnimator的prepareSurfaceLocked()方法,该则真正的处理触发surface show的逻辑。主要分为两部分。
1.将计算的alpha应用于当前surface。
2.判断是否调用showSurfaceRobustlyLocked将surface show出来。
代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
//首先判断是否有SurfaceControl
if (!hasSurface()) {
......
return;
}
//设置mShowAlpha
computeShownFrameLocked();
//判断parentWindow是否hidden,或者当前窗口是否on-screen
if (w.isParentWindowHidden() || !w.isOnScreen()) {
......
} else if (mLastAlpha != mShownAlpha
|| mLastHidden) {
mLastAlpha = mShownAlpha;
ProtoLog.i(WM_SHOW_TRANSACTIONS,
"SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
mSurfaceController, mShownAlpha, w.mHScale, w.mVScale, w);
/*1.设置surface的alpha*/
boolean prepared =
mSurfaceController.prepareToShowInTransaction(t, mShownAlpha);
//如果当前状态为HAS_DRAWN
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
/*2.触发show surface*/
if (showSurfaceRobustlyLocked(t)) {
mAnimator.requestRemovalOfReplacedWindows(w);
//设置mLastHidden为false
mLastHidden = false;
.......
} else {
w.setOrientationChanging(false);
}
}
}
} else {
if (mWin.isAnimating(TRANSITION | PARENTS)) {
ProtoLog.v(WM_DEBUG_ANIM, "prepareSurface: No changes in animation for %s", this);
}
}
......
}
从上述代码中可以看出触发showSurfaceRobustlyLocked的判断条件有以下几点: 1.w.isParentWindowHidden判断其parent的mHidden是否为true,此时当前窗口没有parent直接返回false
2.w.isOnScreen,判断当前窗口是否在屏幕上,如果该窗口mVisible为true或者在不可见之前正在运行动画,判断为在屏幕上。我们在上次布局的AppTransitionController.handleAppTransitionReady阶段将当前窗口的mVisible置为了true,因此w.isOnScreen返回true。
3.mLastAlpha != mShownAlpha以及mLastHidden满足其一即可,此处我们分析mLastHidden,该标志位在创建SurfaceControl或者hide surface时会被置为true,因为当前窗口才刚刚被创建,因此mLastHidden为true。
经过以上判断可以得出我们顺利触发showSurfaceRobustlyLocked 后面通过WindowStateAnimator将show SurfaceControl的请求传递给了WindowSurfaceController
/**
* Have the surface flinger show a surface, robustly dealing with
* error conditions. In particular, if there is not enough memory
* to show the surface, then we will try to get rid of other surfaces
* in order to succeed.
*
* @return Returns true if the surface was successfully shown.
*/
private boolean showSurfaceRobustlyLocked(SurfaceControl.Transaction t) {
//WindowStateAnimator将show SurfaceControl的请求传递给了WindowSurfaceController
//调用WindowSurfaceController的showRobustly方法
boolean shown = mSurfaceController.showRobustly(t);
//如果没有成功返回false
if (!shown)
return false;
t.merge(mPostDrawTransaction);
return true;
}
在WindowSurfaceController中,首先判断标志位mSurfaceShown,若为true则直接返回;若为false,则将mSurfaceShown置为true,并调用SurfaceControl.show。至此真正的绘图已经显示出来,但是否真正的被用户看见,还需要看其parent是否被show。 代码路径:framework/services/core/java/com/android/server/wm/WindowSurfaceController.java
boolean showRobustly(SurfaceControl.Transaction t) {
......
//首先判断surface是否已经shown
if (mSurfaceShown) {
return true;
}
//将mSurfaceShown设置为true
setShown(true);
//调用SurfceControl中的show方法,将surface show出来
t.show(mSurfaceControl);
if (mAnimator.mIsWallpaper) {
EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
mAnimator.mWin.getDisplayId(), 1 /* request shown */);
}
return true;
}
从SurfaceControl的创建以及show的流程上看,可以发现WMS是通过WindowSurfaceController对SurfaceControl进行管理的。 最后我们看一下SurfaceControl中的show方法 代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
* Request that a given surface and it's sub-tree be shown.
*
* @param sc The surface to show.
* @return This transaction.
* @hide
*/
@UnsupportedAppUsage
public Transaction show(SurfaceControl sc) {
checkPreconditions(sc);
nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
return this;
}