WMS-窗口relayout&FinishDrawing

1. Relayout

前面介绍过当应用进程Resume的时候,会走到ViewRootImpl.javasetView, 在其中会调用到WMS的addWindow,其中会创建WindowState对象,将其挂载到窗口层级树上,并将WindowState对象和对应的Client(在ViewRootImpl中的W类对象,继承自binder,可跨进程传递) 存入到WMS的

HashMap<IBinder, WindowState> mWindowMap

ViewRootImpl.javasetView中,还会调用requestLayout,其中会触发一次vsync,当vsync周期到时,会回调到ViewRootImpl.javaperformTraversals函数,在其中又走到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是负责显示的。

WindowStateforAllWindows,会调用到applyInOrderWithImeWindows,在其中会调用参数callbackapply,

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.javalayoutWindowLw是 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对象,然后调用WindowStatefinishDrawing,其中又会调用WindowStateAnimator.javafinishDrawingLocked, 里面会设置:

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.mHasSurfacerequestLayout阶段就创建了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.javaperformShowLocked中会设置:

mWinAnimator.mDrawState = HAS_DRAWN;

mDrawState = HAS_DRAWN:一旦设置,窗口永久标记为 "已首次显示",后续仅刷新内容,不再走首次显示流程。

这个方法是 Android 窗口显示的 "最终开关",所有上游的布局计算、状态流转、Surface 准备,最终都通过这一步触发窗口真正显示在屏幕上。

虽然状态设置为HAS_DRAWN,但其实并没有真正show出来,因为没有看到和surfaceflinger的交互,还要继续往下看。

2.4 prepareSurfaces

回到2.2, DisplayContent.javaapplySurfaceChangesTransaction,在执行完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.javaprepareSurfaces,然后再调用所有子类的prepareSurfaces,熟悉的套路。

最后走到子类WindowState.javaprepareSurfaces,在里面调用mWinAnimator.prepareSurfaceLocked(getSyncTransaction());

最后一步步调用到WindowSurfaceController.javashowRobustly

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

相关推荐
baidu_247438614 小时前
Android ViewModel定时任务
android·开发语言·javascript
有位神秘人5 小时前
Android中Notification的使用详解
android·java·javascript
·云扬·5 小时前
MySQL Binlog落盘机制深度解析:性能与安全性的平衡艺术
android·mysql·adb
独自破碎E6 小时前
【BISHI9】田忌赛马
android·java·开发语言
代码s贝多芬的音符7 小时前
android 两个人脸对比 mlkit
android
darkb1rd9 小时前
五、PHP类型转换与类型安全
android·安全·php
gjxDaniel9 小时前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
csj509 小时前
安卓基础之《(22)—高级控件(4)碎片Fragment》
android
峥嵘life10 小时前
Android16 【CTS】CtsMediaCodecTestCases等一些列Media测试存在Failed项
android·linux·学习