1. Relayout
前面介绍过当应用进程Resume的时候,会走到ViewRootImpl.java的setView, 在其中会调用到WMS的addWindow,其中会创建WindowState对象,将其挂载到窗口层级树上,并将WindowState对象和对应的Client(在ViewRootImpl中的W类对象,继承自binder,可跨进程传递) 存入到WMS的
HashMap<IBinder, WindowState> mWindowMap
在ViewRootImpl.java的setView中,还会调用requestLayout,其中会触发一次vsync,当vsync周期到时,会回调到ViewRootImpl.java的performTraversals函数,在其中又走到relayoutWindow,在relayoutWindow里调用mWindowSession.relayout,通过WindowSession调用到
WMS的relayoutWindow。relayoutWindow函数代码非常多,所以还是分段分析:
1.1 get WindowState
java
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
...
// 1
final WindowState win = windowForClientLocked(session, client, false);
...
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
// 2
win.setRequestedSize(requestedWidth, requestedHeight);
}
...
// 3
win.setViewVisibility(viewVisibility);
mark 1: 通过ViewRootImpl传递来的client(ViewRootImpl中的W类对象),从mWindowMap取到WindowState对象。
mark 2: 把应用端请求的大小,保存到WindowState下.
mark 3: 设置窗口可见 viewVisibility = VISIBLE
1.2 createSurfaceControl
java
if (shouldRelayout) {
try {
// 1
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
...
}
java
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
...
WindowSurfaceController surfaceController;
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
// 1 创建"Buff"类型Surface
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (surfaceController != null) {
// 2 赋值给`outSurfaceControl`,`outSurfaceControl`是出参。
surfaceController.getSurfaceControl(outSurfaceControl);
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
...
}
java
// WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked(int windowType) {
...
// 1
resetDrawState();
...
// 2
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
height, format, flags, this, windowType);
mark 1: resetDrawState 里面会设置mDrawState = DRAW_PENDING;
mark 2: 真正创建SurfaceControl的地方,创建后设置为"Buff"图层
下面介绍一下
mDrawState的取值和涵义:NO_SURFACE = 0, 窗口还未创建 Surface(绘制载体),是窗口初始化的初始状态;窗口隐藏 / 销毁后也会回到此状态。
DRAW_PENDING = 1, Surface 已创建,但窗口还未开始首次绘制;此时 Surface 处于隐藏状态,屏幕上不可见。
COMMIT_DRAW_PENDING = 2, 窗口已完成首次绘制,但绘制结果还未提交;Surface 仍隐藏,需等下一次布局执行时提交绘制结果。
READY_TO_SHOW = 3, 窗口绘制已提交,但 Surface 暂不显示;用于 "令牌级窗口同步"(比如 Activity 下所有窗口都准备好后再一起显示,避免闪屏)
HAS_DRAWN = 4, 窗口的 Surface 已真正显示在屏幕上,完成 "首次显示";后续窗口刷新 / 重绘不会改变此状态(仅标记首次显示完成)。
1.3 performSurfacePlacement 计算窗口大小
继续分析WMS的relayoutWindow
java
mWindowPlacerLocked.performSurfacePlacement(true /* force */); // WindowManagerService.java
performSurfacePlacementLoop(); // WindowSurfacePlacer.java
mService.mRoot.performSurfacePlacement(); // WindowSurfacePlacer.java
performSurfacePlacementNoTrace(); // RootWindowContainer.java
dc.applySurfaceChangesTransaction(); // RootWindowContainer.java 遍历所有显示设备,让每个设备处理旗下窗口的 Surface 变更
performLayout(repeats == 1, false /* updateInputWindows */); // DisplayContent.java
performLayoutNoTrace(initial, updateInputWindows); // DisplayContent.java
forAllWindows(mPerformLayout, true /* traverseTopToBottom */); // DisplayContent.java
forAllWindows(wrapper, traverseTopToBottom); // WindowContainer.java
////////////////////////////
// WindowContainer.java
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
if (traverseTopToBottom) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
return true;
}
}
} else {
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
return true;
}
}
}
return false;
}
// WindowState.java
@Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
if (mChildren.isEmpty()) {
// The window has no children so we just return it.
return applyInOrderWithImeWindows(callback, traverseTopToBottom);
}
if (traverseTopToBottom) {
return forAllWindowTopToBottom(callback);
} else {
return forAllWindowBottomToTop(callback);
}
}
// WindowState.java
private boolean applyInOrderWithImeWindows(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
if (traverseTopToBottom) {
if (applyImeWindowsIfNeeded(callback, traverseTopToBottom)
|| callback.apply(this)) {
return true;
}
} else {
if (callback.apply(this)
|| applyImeWindowsIfNeeded(callback, traverseTopToBottom)) {
return true;
}
}
return false;
}
forAllWindows的实现又是一个递归,来遍历窗口层级树。我们之前分析过这样的情形。在WindowContainer的子类里面,虽然也有实现forAllWindows,但
最后还是调用父类WindowContainer的实现,从而继续递归。只有在子类WindowState中,它的实现是打破了递归的,并且这样也合理,因为其他的窗口层级节点都是window容器,WindowState是负责显示的。
WindowState的forAllWindows,会调用到applyInOrderWithImeWindows,在其中会调用参数callback的apply,
callback 是上面走到DisplayContent,在调用performLayoutNoTrace时传递的mPerformLayout,
mPerformLayout是一个Consumer<WindowState>对象(理解为一个lambda表达式对象),那么上面调用它的apply,其实就是执行这个对象,
java
// DisplayContent.java
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
...
getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
...
};
DisplayPolicy.java的layoutWindowLw是 WMS 中单个窗口 Frame(位置 / 大小)计算的最终实现,涵盖系统栏、刘海屏、输入法、厂商定制等所有场景的适配。比如你想让弹窗避开系统栏,便可以在该函数中做统一处理重新计算窗口位置以及宽高。
1.4 返回窗口大小给应用端
java
win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,
false /* useLatestConfig */, shouldRelayout);
将前面1.3 中计算好的窗口大小复制给outFrames,而该参数又是从app侧传递过来的。所以最后就将计算好的窗口大小返回给了APP。
2. FinishDrawing
在上面relayout结束后,应用进程就开始draw了,draw之后会调用WMS的finishDrawingWindow
java
// WindowManagerService.java
void finishDrawingWindow(Session session, IWindow client,
...
WindowState win = windowForClientLocked(session, client, false);
...
if (win != null && win.finishDrawing(postDrawTransaction)) {
...
mWindowPlacerLocked.requestTraversal();
}
...
}
2.1 COMMIT_DRAW_PENDING
在finishDrawingWindow中,也会通过HashMap<IBinder, WindowState> mWindowMap先找到对应的WindowState对象,然后调用WindowState的finishDrawing,其中又会调用WindowStateAnimator.java的finishDrawingLocked, 里面会设置:
mDrawState = COMMIT_DRAW_PENDING;
这里要说一下,每个WindowState对应一个WindowStateAnimator。
2.2 requestTraversal
之后调用mWindowPlacerLocked.requestTraversal();
java
requestTraversal() // WindowSurfacePlacer.java
mService.mAnimationHandler.post(mPerformSurfacePlacement); // WindowSurfacePlacer.java
performSurfacePlacement(); // WindowSurfacePlacer.java
performSurfacePlacement(false /* force */); // WindowSurfacePlacer.java
performSurfacePlacementLoop() // WindowSurfacePlacer.java
mService.mRoot.performSurfacePlacement(); // WindowSurfacePlacer.java
performSurfacePlacementNoTrace(); // RootWindowContainer.java
mWmService.openSurfaceTransaction();
applySurfaceChangesTransaction() // RootWindowContainer.java
dc.applySurfaceChangesTransaction(); // DisplayContent.java
// 1
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
// 2
prepareSurfaces();
mWmService.openSurfaceTransaction(); // 提交事物
上面的调用栈和relayout流程中,都到了一起,都走到了performSurfacePlacement函数,这2次的调用肯定会根据一些状态变量觉得哪次调用走,哪次不走。后续分析。
mark 1:前面我们已经分析过forAllWindows的套路,所以不再分析,直接看最后执行的Consumer对象mApplySurfaceChangesTransaction,
2.3 mApplySurfaceChangesTransaction
核心作用:单个窗口 Surface 事务的核心应用入口,完成遮挡状态、显示属性、绘制提交、壁纸适配等状态同步,是 "布局计算" 到 "Surface 渲染" 的关键桥梁;
核心逻辑:
- 遮挡优化:标记完全遮挡屏幕的窗口,减少后方窗口渲染开销;
- 状态流转:通过
commitFinishDrawingLocked推进绘制状态,为performShowLocked铺路; - 联动适配:同步壁纸可见性、Activity 绘制状态、信标窗口,保证显示一致性;
java
// DisplayContent.java
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
...
if (w.mHasSurface) {
// Take care of the window being ready to display.
// 参考2.3.1
final boolean committed = winAnimator.commitFinishDrawingLocked();
...
w.mHasSurface 在requestLayout阶段就创建了surface,并设置 = true 了,所以会走进winAnimator.commitFinishDrawingLocked。
2.3.1 READY_TO_SHOW
java
// WindowStateAnimator.java
boolean commitFinishDrawingLocked() {
...
// 1
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
// 2
result = mWin.performShowLocked();
}
return result;
mark 1: 重要的状态设置:
mDrawState = READY_TO_SHOW;
mark 2: 如果是系统窗口或者activity.canShowWindows == true(大多数情况下为true),走进mWin.performShowLocked,
2.3.2 HAS_DRAWN
WindowState.java的performShowLocked中会设置:
mWinAnimator.mDrawState = HAS_DRAWN;
mDrawState = HAS_DRAWN:一旦设置,窗口永久标记为 "已首次显示",后续仅刷新内容,不再走首次显示流程。
这个方法是 Android 窗口显示的 "最终开关",所有上游的布局计算、状态流转、Surface 准备,最终都通过这一步触发窗口真正显示在屏幕上。
虽然状态设置为HAS_DRAWN,但其实并没有真正show出来,因为没有看到和surfaceflinger的交互,还要继续往下看。
2.4 prepareSurfaces
回到2.2, DisplayContent.java的applySurfaceChangesTransaction,在执行完lamda表达式mApplySurfaceChangesTransaction之后,
继续执行prepareSurfaces:
java
// DisplayContent
@Override
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
// 1. 拿到事务
final Transaction transaction = getPendingTransaction();
// 2. 调用父类方法
super.prepareSurfaces();
// 3. 把事务merge到全局事务,供后续统一处理
SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
mark 2中最后会走到WindowContainer.java的prepareSurfaces,然后再调用所有子类的prepareSurfaces,熟悉的套路。
最后走到子类WindowState.java的prepareSurfaces,在里面调用mWinAnimator.prepareSurfaceLocked(getSyncTransaction());。
最后一步步调用到WindowSurfaceController.java的showRobustly
java
boolean showRobustly(SurfaceControl.Transaction t) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+ " during relayout");
if (mSurfaceShown) {
return true;
}
setShown(true);
t.show(mSurfaceControl);
return true;
}
1、这个日志很关键,表示 Framework 已经将 Surface 提交到 SurfaceFlinger 了。(严格来说需要等后面事务的apply)
2、将 mSurfaceShown 变量设置为true, 这个也是分析黑屏问题dump要看第一个关键变量,如果为 false 说明窗口并没有显示,可能是被遮挡了
3、这里看到 SurfaceControl.Transaction::show 的调用地方了, 这个 show 就说明需要把 Suface 显示, 也是 finishDrawingWindow 最终的结果。
2.5 提交事物
最后会到2.2的performSurfacePlacementNoTrace
在走完applySurfaceChangesTransaction后,调用mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
这里面会提交事物给sf