书接上文,面试官咨询我窗口的构建流程,我嗯啊战神表示不知。
面试官问 你是如何判断当前窗口的状态呢?
嗯啊战神的我大喜 ,我dumpsys 一下window的状态 mDrawState为HAS_DRAWN就代表OK了。
面试官又问,窗口从创建到首次显示的完整流程中,mDrawState的状态是如何变化的?
请允许我去看下源码 ,谁记介个啊!
mDrawState 是在WindowStateAnimator类里,我好奇为啥需要会放在这个类里,而不是放在WindowState里
我查了下AI,是这么说的
WindowStateAnimator 是 WindowState 的一个辅助类,专门处理与窗口相关的动画逻辑和绘制状态管理。
### **为什么将 mDrawState 放在 WindowStateAnimator 中?**
mDrawState 是一个字段,用于跟踪窗口的绘制状态(例如 NO_SURFACE, DRAW_PENDING, COMMIT_DRAW_PENDING, READY_TO_SHOW, HAS_DRAWN 等)。它被放置在 WindowStateAnimator 中,而不是直接放在 WindowState 中,有以下几个原因:
#### **(1) 职责分离与模块化**
- WindowState 负责管理窗口的元数据和状态(如窗口的大小、位置、层级、类型等),而 WindowStateAnimator 专注于动画和绘制相关的逻辑。
- mDrawState 是与窗口绘制流程直接相关的状态,涉及 Surface 的创建、内容的提交以及动画的执行。将它放在 WindowStateAnimator 中可以让绘制相关的逻辑更加集中,避免 WindowState 类变得过于复杂。
- 这种设计符合单一职责原则(Single Responsibility Principle),使代码更易于维护和扩展。
#### **(2) 动画与绘制状态的紧密耦合**
- 窗口的绘制状态(如 DRAW_PENDING 或 HAS_DRAWN)直接影响动画的执行。例如,窗口只有在内容绘制完成(HAS_DRAWN)后才能开始某些动画。
- WindowStateAnimator 负责管理动画的生命周期,因此它需要直接访问和更新 mDrawState,以确保动画与绘制状态同步。例如,当窗口的 Surface 准备好并完成首次绘制后,mDrawState 会变为 HAS_DRAWN,从而触发后续的动画逻辑。
- 如果 mDrawState 放在 WindowState 中,WindowStateAnimator 每次操作都需要通过 WindowState 访问,增加耦合度和复杂性。
#### **(3) 性能优化**
- 绘制状态的频繁更新通常发生在动画或窗口内容变化的场景中,而 WindowStateAnimator 是这些操作的直接执行者。将 mDrawState 放在 WindowStateAnimator 中可以减少跨对象访问的开销,提高性能。
- 此外,WindowStateAnimator 与 SurfaceFlinger 的交互更直接,能够更高效地管理绘制状态的转换。
#### **(4) 历史与设计演变**
- 在 AOSP 的早期版本中,窗口管理的逻辑可能较为简单,但随着 Android 系统的复杂性增加,动画和绘制逻辑变得更加独立和复杂。WindowStateAnimator 作为一个专门的类被引入,用于隔离这些逻辑。
- mDrawState 被放在 WindowStateAnimator 中,可能是历史设计演变的结果,反映了 Android 团队对窗口管理模块化的持续优化
mDrawState 在 WindowStateAnimator 里的状态值。
//services/core/java/com/android/server/wm/WindowStateAnimator.java
/** This is set when there is no Surface */
/** 当不存在 Surface 时设置此值 */
static final int NO_SURFACE = 0;
/** This is set after the Surface has been created but before the window has been drawn. During
// this time the surface is hidden */
static final int DRAW_PENDING = 1;
/** This is set after the window has finished drawing for the first time but before its surface
* is shown. The surface will be displayed when the next layout is run. */
static final int COMMIT_DRAW_PENDING = 2;
/** This is set during the time after the window's drawing has been committed, and before its
* surface is actually shown. It is used to delay showing the surface until all windows in a
* token are ready to be shown. */
static final int READY_TO_SHOW = 3;
/** Set when the window has been shown in the screen the first time. */
static final int HAS_DRAWN = 4;
NO_SURFACE: 0 当不存在Surface时设置此值。DRAW_PENDING: 1在Surface已创建但窗口尚未绘制完成时设置此值。在此期间,Surface处于隐藏状态。COMMIT_DRAW_PENDING: 2 当下一次布局运行时,Surface将被显示。READY_TO_SHOW:3在窗口绘制已提交之后、Surface实际显示之前的时间段内设置此值。HAS_DRAWN: 4 当窗口首次在屏幕上显示时设置此值。
好吧,那我就简单看下如何从NO_SURFACE:0到HAS_DRAWN:4的吧。
ViewRootImpl在调用 setView()的时候 会调用到 requestLayout()
0-1 NO_SURFACE 到 DRAW_PENDING
代码基础流程
schedule1Traversals()->
TraversalRunnable()->
doTraversal()
performTraversals()->
relayoutWindow()->
Session.relayout()->
wms.relayoutWindow()
我们看下WMS的 relayoutWindow()
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) {
//outSurfaceControl 在 viewRootImp 里面构建
//省略
//调用 WindowStateAnimator.createSurfaceControl 此刻 mDrawState = DRAW_PENDING;
//进行了native层的copy
// -> resetDrawState(); 该方法里将状态推进到 DRAW_PENDING
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
//省略 ...
//这里完成层级的遍历 和布局测量等 确定了显示的区域
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
//省略
//把算出来的数据给人返回回去 ,毕竟人只传了个空壳子过来 回填给了应用 干完这事之后 surfacecontrol 就可以用了
getInsetsSourceControls(win, outActiveControls);
}
该方法调用到了
surfaceController = winAnimator.createSurfaceLocked();
进入
WindowStateAnimator
WindowSurfaceController createSurfaceLocked() {
final WindowState w = mWin;
//避免多次构建
if (mSurfaceController != null) {
return mSurfaceController;
}
w.setHasSurface(false);
//这里把 mDrawState = DRAW_PENDING; 进入到1
resetDrawState();
//省略....
// Set up surface control with initial size.
try {
//构建WindowSurfaceController
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
flags, this, attrs.type);
mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
w.setHasSurface(true);
w.mInputWindowHandle.forceChange();
} catch (OutOfResourcesException e) {
mDrawState = NO_SURFACE;
return null;
} catch (Exception e) {
Slog.e(TAG, "Exception creating surface (parent dead?)", e);
mDrawState = NO_SURFACE;
return null;
}
return mSurfaceController;
}
resetDrawState()方法 把mDrawState = DRAW_PENDING; 进入到1的状态,毕竟int默认是0- 构建了
WindowSurfaceController
我当时看到这里的时候,脑子很糊涂,我咋记得在addWindow()的时候 不是构建了一个SurfaceControl 么,咋这里又构建一个?
其实在addWindow() 其实构建了一个SurfaceControl,这里才是构建Surface相关,这里不展开。
WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
int windowType) {
mAnimator = animator;
title = name;
mService = animator.mService;
final WindowState win = animator.mWin;
mWindowType = windowType;
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl())
.setName(name)
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
if (useBLAST) {
b.setBLASTLayer();
}
mSurfaceControl = b.build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
1-2 DRAW_PENDING 到 COMMIT_DRAW_PENDING
那什么时候会推进到 COMMIT_DRAW_PENDING 也就是2 呢。
我看了下 代码 只有WindowStateAnimator的 finishDrawingLocked()方法里将值改为了COMMIT_DRAW_PENDING
一路倒查,Session.finishDrawing()
好吧 ,我已经知道Session是 ViewRootImpl和 WMS的桥梁了。
ViewRootImpl里面俩方法调用
reportDrawFinished()。看了下reportDrawFinished()被performTraversals()调用。
代码流程图如下
在WindowStateAnimator.finishDrawingLocked()
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
boolean layoutNeeded = false;
//在laytouWindow 里面推到了DRAW_PENDING
if (mDrawState == DRAW_PENDING) {
//将状态推进到COMMIT_DRAW_PENDING 也就是2
mDrawState = COMMIT_DRAW_PENDING;
layoutNeeded = true;
}
if (postDrawTransaction != null) {
if (mLastHidden && mDrawState != NO_SURFACE) {
mPostDrawTransaction.merge(postDrawTransaction);
layoutNeeded = true;
} else {
postDrawTransaction.apply();
}
}
return layoutNeeded;
}
简单理解,我们调用测量、布局、绘制完成,会和wms 交互,告知wsm,我已提交,可以绘制。
2-3 COMMIT_DRAW_PENDING 到 READY_TO_SHOW
在 wms.finishDrawingWindow()
if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
if (win.hasWallpaper()) {
win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
win.setDisplayLayoutNeeded();
mWindowPlacerLocked.requestTraversal();
}
mWindowPlacerLocked.requestTraversal();
//会走全局遍历的那套方法
void requestTraversal() {
if (mTraversalScheduled) {
return;
}
mTraversalScheduled = true;
if (mDeferDepth > 0) {
mDeferredRequests++;
return;
}
mService.mAnimationHandler.post(mPerformSurfacePlacement);
}
//走之前遍历那套
--->mPerformSurfacePlacement
-->performSurfacePlacement()
---> performSurfacePlacement(false /* force */);
--> performSurfacePlacementLoop();
--> mService.mRoot.performSurfacePlacement();
--> RootWindowContainer.performSurfacePlacement()
-->performSurfacePlacementNoTrace();
--> applySurfaceChangesTransaction();
-->dc.applySurfaceChangesTransaction();
-->DisplayContent.applySurfaceChangesTransaction()
//1.2.1
-->forAllWindows(mApplySurfaceChangesTransaction, true );//底部转圈遍历
->mApplySurfaceChangesTransaction(){
1.2.2//推进状态到READY_TO_SHOW
WindowStateAnimator.commitFinishDrawingLocked()
}
//SF提交事务真正的绘制,也就说状态推进过去,但是还没画呢
prepareSurfaces();
mApplySurfaceChangesTransaction()
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
if (w.mHasSurface) {
//w,mHasSurface 在第一次构建的时候 设置为了 true createSurfaceLocked()里面
final boolean committed = winAnimator.commitFinishDrawingLocked();
}
}
WindowStateAnimator.commitFinishDrawingLocked()
boolean commitFinishDrawingLocked() {
//已经是COMMIT_DRAW_PENDING 了 所以这里进不去
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
mDrawState = READY_TO_SHOW;//3
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
//进入显示
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
//不是activity 直接展示
result = mWin.
performShowLocked();
}
return result;
}
}
简单总结,从 RootWindowContainer 遍历查找 需要进行变动的的window(),都遍历找出来,然后找到目标将mDrawState 推进到 READY_TO_SHOW,也就是3。
3-4 READY_TO_SHOW 到 HAS_DRAWN
WindowState.performShowLocked()
//在 commitFinishDrawingLocked里面调用的
WindowState.performShowLocked(){
final int drawState = mWinAnimator.mDrawState;
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);
} else {
mActivityRecord.onStartingWindowDrawn();
}
}
mWmService.enableScreenIfNeededLocked();
//本地动画的构建
mWinAnimator.applyEnterAnimationLocked();
//直接显示了
mWinAnimator.mDrawState = HAS_DRAWN;
//把自己可见,把自己的孩子可见。
if (mHidden) {
mHidden = false;
final DisplayContent displayContent = getDisplayContent();
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = mChildren.get(i);
if (c.mWinAnimator.mSurfaceController != null) {
c.performShowLocked();
if (displayContent != null) {
displayContent.setLayoutNeeded();
}
}
}
}
总结 mDrawState推进到 HAS_DRAWN 4,然后让自己可见, 如果mChildren>0 ,也会遍历去执行performShowLocked()
prepareSurfaces();
事务的提交,只有提交到SurfaceFlinger才能进行真正的绘制。
@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);
}
简单总结
- 0-1 主要是构建
WindowSurfaceController,并准备好对应的参数。 - 1-2 走完测量、布局、绘制, 会告知
WMS,我View层已经OK,数据都给你了,剩下看你的了。 - 2-3 从根节点查看所有需要变动的
window,遍历将状态推进到3。 - 3-4 看看当前情况是可以显示,如果可以显示,直接推进到4,这里如果有动画会延迟的动画结束。
最后 事务提交,球踢给
SurfaceFlinger。
(自己画的图,丑了点,勿怪)
如果能重来,我肯定这么回答。
问题 1:你是如何判断当前窗口的状态?
- 我会使用
dumpsys window查看窗口的mDrawState。例如,如果mDrawState是HAS_DRAWN(4),表示窗口已成功绘制并显示到屏幕。如果是DRAW_PENDING(1)或COMMIT_DRAW_PENDING(2),说明还在绘制流程中,尚未显示。
问题 2:窗口从创建到首次显示的完整流程中,mDrawState 的状态是如何变化的?
-
窗口从创建到显示,
mDrawState在WindowStateAnimator中经历以下状态:NO_SURFACE (0):初始状态,无 Surface。在addWindow或relayoutWindow中调用createSurfaceLocked,创建SurfaceControl,推进到DRAW_PENDING。DRAW_PENDING (1):Surface已创建,等待应用绘制。ViewRootImpl完成measure/layout/draw,通过reportDrawFinished通知 WMS,调用finishDrawingLocked,推进到COMMIT_DRAW_PENDING。COMMIT_DRAW_PENDING (2):应用提交绘制内容,WMS通过RootWindowContainer遍历( performSurfacePlacement),调用commitFinishDrawingLocked,推进到READY_TO_SHOW。READY_TO_SHOW (3):窗口准备显示,performShowLocked检查条件(例如可见性、动画),调用SurfaceControl.show,推进到HAS_DRAWN。HAS_DRAWN (4):窗口显示到屏幕,递归显示子窗口。
-
最后,
WMS通过prepareSurfaces和SurfaceControl.Transaction提交事务给SurfaceFlinger,完成渲染。
最后默默把简历改为熟悉WMS的拼写