Android Framework-WMS-mDrawState四步走

227 阅读8分钟

image.png

书接上文,面试官咨询我窗口的构建流程,我嗯啊战神表示不知。

面试官问 你是如何判断当前窗口的状态呢? 嗯啊战神的我大喜 ,我dumpsys 一下window的状态 mDrawStateHAS_DRAWN就代表OK了。

面试官又问,窗口从创建到首次显示的完整流程中,mDrawState的状态是如何变化的?

943b50a5a00367d214e52818a6cac24b.jpg

请允许我去看下源码 ,谁记介个啊!

mDrawState 是在WindowStateAnimator类里,我好奇为啥需要会放在这个类里,而不是放在WindowState里 我查了下AI,是这么说的

WindowStateAnimatorWindowState 的一个辅助类,专门处理与窗口相关的动画逻辑和绘制状态管理。
### **为什么将 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_PENDINGHAS_DRAWN)直接影响动画的执行。例如,窗口只有在内容绘制完成(HAS_DRAWN)后才能开始某些动画。
-   WindowStateAnimator 负责管理动画的生命周期,因此它需要直接访问和更新 mDrawState,以确保动画与绘制状态同步。例如,当窗口的 Surface 准备好并完成首次绘制后,mDrawState 会变为 HAS_DRAWN,从而触发后续的动画逻辑。
-   如果 mDrawState 放在 WindowState 中,WindowStateAnimator 每次操作都需要通过 WindowState 访问,增加耦合度和复杂性。
#### **(3) 性能优化**
-   绘制状态的频繁更新通常发生在动画或窗口内容变化的场景中,而 WindowStateAnimator 是这些操作的直接执行者。将 mDrawState 放在 WindowStateAnimator 中可以减少跨对象访问的开销,提高性能。
-   此外,WindowStateAnimatorSurfaceFlinger 的交互更直接,能够更高效地管理绘制状态的转换。

#### **(4) 历史与设计演变**
-   在 AOSP 的早期版本中,窗口管理的逻辑可能较为简单,但随着 Android 系统的复杂性增加,动画和绘制逻辑变得更加独立和复杂。WindowStateAnimator 作为一个专门的类被引入,用于隔离这些逻辑。
-   mDrawState 被放在 WindowStateAnimator 中,可能是历史设计演变的结果,反映了 Android 团队对窗口管理模块化的持续优化

mDrawStateWindowStateAnimator 里的状态值。

//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:0HAS_DRAWN:4的吧。

ViewRootImpl在调用 setView()的时候 会调用到 requestLayout()

0-1 NO_SURFACEDRAW_PENDING

代码基础流程

schedule1Traversals()->
TraversalRunnable()->
doTraversal()
performTraversals()->
relayoutWindow()->
Session.relayout()->
wms.relayoutWindow()

我们看下WMSrelayoutWindow()

 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 么,咋这里又构建一个?

image.png

其实在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_PENDINGCOMMIT_DRAW_PENDING

那什么时候会推进到 COMMIT_DRAW_PENDING 也就是2 呢。 我看了下 代码 只有WindowStateAnimatorfinishDrawingLocked()方法里将值改为了COMMIT_DRAW_PENDING 一路倒查,Session.finishDrawing() 好吧 ,我已经知道SessionViewRootImplWMS的桥梁了。 ViewRootImpl里面俩方法调用
reportDrawFinished()。看了下reportDrawFinished()performTraversals()调用。

代码流程图如下

image.png

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_PENDINGREADY_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_SHOWHAS_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

mDrawState的推进 .png

(自己画的图,丑了点,勿怪)

如果能重来,我肯定这么回答。

问题 1:你是如何判断当前窗口的状态?

  • 我会使用 dumpsys window 查看窗口的 mDrawState。例如,如果 mDrawStateHAS_DRAWN(4),表示窗口已成功绘制并显示到屏幕。如果是 DRAW_PENDING(1)COMMIT_DRAW_PENDING(2),说明还在绘制流程中,尚未显示。

问题 2:窗口从创建到首次显示的完整流程中,mDrawState 的状态是如何变化的?

  • 窗口从创建到显示,mDrawStateWindowStateAnimator 中经历以下状态:

    1. NO_SURFACE (0) :初始状态,无 Surface。在 addWindow relayoutWindow 中调用 createSurfaceLocked ,创建 SurfaceControl ,推进到 DRAW_PENDING
    2. DRAW_PENDING (1)Surface 已创建,等待应用绘制。 ViewRootImpl 完成 measure/layout/draw ,通过 reportDrawFinished 通知 WMS,调用 finishDrawingLocked ,推进到 COMMIT_DRAW_PENDING
    3. COMMIT_DRAW_PENDING (2) :应用提交绘制内容, WMS 通过 RootWindowContainer 遍历 ( performSurfacePlacement) ,调用 commitFinishDrawingLocked ,推进到 READY_TO_SHOW
    4. READY_TO_SHOW (3) :窗口准备显示, performShowLocked 检查条件(例如可见性、动画),调用 SurfaceControl.show ,推进到 HAS_DRAWN
    5. HAS_DRAWN (4) :窗口显示到屏幕,递归显示子窗口。
  • 最后,WMS 通过 prepareSurfacesSurfaceControl.Transaction 提交事务给 SurfaceFlinger,完成渲染。

最后默默把简历改为熟悉WMS的拼写