news 2026/6/15 2:52:21

WMS-窗口relayoutFinishDrawing

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WMS-窗口relayoutFinishDrawing

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

publicintrelayoutWindow(Sessionsession,IWindowclient,LayoutParamsattrs,intrequestedWidth,intrequestedHeight,intviewVisibility,intflags,longframeNumber,ClientWindowFramesoutFrames,MergedConfigurationmergedConfiguration,SurfaceControloutSurfaceControl,InsetsStateoutInsetsState,InsetsSourceControl[]outActiveControls,PointoutSurfaceSize){...// 1finalWindowStatewin=windowForClientLocked(session,client,false);...WindowStateAnimatorwinAnimator=win.mWinAnimator;if(viewVisibility!=View.GONE){// 2win.setRequestedSize(requestedWidth,requestedHeight);}...// 3win.setViewVisibility(viewVisibility);

mark 1: 通过ViewRootImpl传递来的client(ViewRootImpl中的W类对象),从mWindowMap取到WindowState对象。

mark 2: 把应用端请求的大小,保存到WindowState下.

mark 3: 设置窗口可见 viewVisibility = VISIBLE

1.2 createSurfaceControl

if(shouldRelayout){try{// 1result=createSurfaceControl(outSurfaceControl,result,win,winAnimator);}catch(Exceptione){...}
privateintcreateSurfaceControl(SurfaceControloutSurfaceControl,intresult,WindowStatewin,WindowStateAnimatorwinAnimator){...WindowSurfaceControllersurfaceController;try{Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,"createSurfaceControl");// 1 创建“Buff”类型SurfacesurfaceController=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);...}
// WindowStateAnimator.javaWindowSurfaceControllercreateSurfaceLocked(intwindowType){...// 1resetDrawState();...// 2mSurfaceController=newWindowSurfaceController(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

mWindowPlacerLocked.performSurfacePlacement(true/* force */);// WindowManagerService.javaperformSurfacePlacementLoop();// WindowSurfacePlacer.javamService.mRoot.performSurfacePlacement();// WindowSurfacePlacer.javaperformSurfacePlacementNoTrace();// RootWindowContainer.javadc.applySurfaceChangesTransaction();// RootWindowContainer.java 遍历所有显示设备,让每个设备处理旗下窗口的 Surface 变更performLayout(repeats==1,false/* updateInputWindows */);// DisplayContent.javaperformLayoutNoTrace(initial,updateInputWindows);// DisplayContent.javaforAllWindows(mPerformLayout,true/* traverseTopToBottom */);// DisplayContent.javaforAllWindows(wrapper,traverseTopToBottom);// WindowContainer.java////////////////////////////// WindowContainer.javabooleanforAllWindows(ToBooleanFunction<WindowState>callback,booleantraverseTopToBottom){if(traverseTopToBottom){for(inti=mChildren.size()-1;i>=0;--i){if(mChildren.get(i).forAllWindows(callback,traverseTopToBottom)){returntrue;}}}else{finalintcount=mChildren.size();for(inti=0;i<count;i++){if(mChildren.get(i).forAllWindows(callback,traverseTopToBottom)){returntrue;}}}returnfalse;}// WindowState.java@OverridebooleanforAllWindows(ToBooleanFunction<WindowState>callback,booleantraverseTopToBottom){if(mChildren.isEmpty()){// The window has no children so we just return it.returnapplyInOrderWithImeWindows(callback,traverseTopToBottom);}if(traverseTopToBottom){returnforAllWindowTopToBottom(callback);}else{returnforAllWindowBottomToTop(callback);}}// WindowState.javaprivatebooleanapplyInOrderWithImeWindows(ToBooleanFunction<WindowState>callback,booleantraverseTopToBottom){if(traverseTopToBottom){if(applyImeWindowsIfNeeded(callback,traverseTopToBottom)||callback.apply(this)){returntrue;}}else{if(callback.apply(this)||applyImeWindowsIfNeeded(callback,traverseTopToBottom)){returntrue;}}returnfalse;}

forAllWindows的实现又是一个递归,来遍历窗口层级树。我们之前分析过这样的情形。在WindowContainer的子类里面,虽然也有实现forAllWindows,但

最后还是调用父类WindowContainer的实现,从而继续递归。只有在子类WindowState中,它的实现是打破了递归的,并且这样也合理,因为其他的窗口层级节点都是window容器,WindowState是负责显示的。

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

callback是上面走到DisplayContent,在调用performLayoutNoTrace时传递的mPerformLayout

mPerformLayout是一个Consumer<WindowState>对象(理解为一个lambda表达式对象),那么上面调用它的apply,其实就是执行这个对象,

// DisplayContent.javaprivatefinalConsumer<WindowState>mPerformLayoutAttached=w->{...getDisplayPolicy().layoutWindowLw(w,w.getParentWindow(),mDisplayFrames);...};

DisplayPolicy.javalayoutWindowLw是 WMS 中单个窗口 Frame(位置 / 大小)计算的最终实现,涵盖系统栏、刘海屏、输入法、厂商定制等所有场景的适配。比如你想让弹窗避开系统栏,便可以在该函数中做统一处理重新计算窗口位置以及宽高。

1.4 返回窗口大小给应用端

win.fillClientWindowFramesAndConfiguration(outFrames,mergedConfiguration,false/* useLatestConfig */,shouldRelayout);

将前面1.3 中计算好的窗口大小复制给outFrames,而该参数又是从app侧传递过来的。所以最后就将计算好的窗口大小返回给了APP。

2. FinishDrawing

在上面relayout结束后,应用进程就开始draw了,draw之后会调用WMS的finishDrawingWindow

// WindowManagerService.javavoidfinishDrawingWindow(Sessionsession,IWindowclient,...WindowStatewin=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();

requestTraversal()// WindowSurfacePlacer.javamService.mAnimationHandler.post(mPerformSurfacePlacement);// WindowSurfacePlacer.javaperformSurfacePlacement();// WindowSurfacePlacer.javaperformSurfacePlacement(false/* force */);// WindowSurfacePlacer.javaperformSurfacePlacementLoop()// WindowSurfacePlacer.javamService.mRoot.performSurfacePlacement();// WindowSurfacePlacer.javaperformSurfacePlacementNoTrace();// RootWindowContainer.javamWmService.openSurfaceTransaction();applySurfaceChangesTransaction()// RootWindowContainer.javadc.applySurfaceChangesTransaction();// DisplayContent.java// 1forAllWindows(mApplySurfaceChangesTransaction,true/* traverseTopToBottom */);// 2prepareSurfaces();mWmService.openSurfaceTransaction();// 提交事物

上面的调用栈和relayout流程中,都到了一起,都走到了performSurfacePlacement函数,这2次的调用肯定会根据一些状态变量觉得哪次调用走,哪次不走。后续分析。

mark 1:前面我们已经分析过forAllWindows的套路,所以不再分析,直接看最后执行的Consumer对象mApplySurfaceChangesTransaction

2.3 mApplySurfaceChangesTransaction

核心作用:单个窗口 Surface 事务的核心应用入口,完成遮挡状态、显示属性、绘制提交、壁纸适配等状态同步,是 “布局计算” 到 “Surface 渲染” 的关键桥梁;

核心逻辑

  • 遮挡优化:标记完全遮挡屏幕的窗口,减少后方窗口渲染开销;
  • 状态流转:通过commitFinishDrawingLocked推进绘制状态,为performShowLocked铺路;
  • 联动适配:同步壁纸可见性、Activity 绘制状态、信标窗口,保证显示一致性;
// DisplayContent.javaprivatefinalConsumer<WindowState>mApplySurfaceChangesTransaction=w->{...if(w.mHasSurface){// Take care of the window being ready to display.// 参考2.3.1finalbooleancommitted=winAnimator.commitFinishDrawingLocked();...

w.mHasSurfacerequestLayout阶段就创建了surface,并设置 = true 了,所以会走进winAnimator.commitFinishDrawingLocked

2.3.1 READY_TO_SHOW
// WindowStateAnimator.javabooleancommitFinishDrawingLocked(){...// 1mDrawState=READY_TO_SHOW;booleanresult=false;finalActivityRecordactivity=mWin.mActivityRecord;if(activity==null||activity.canShowWindows()||mWin.mAttrs.type==TYPE_APPLICATION_STARTING){// 2result=mWin.performShowLocked();}returnresult;

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

// DisplayContent@OverridevoidprepareSurfaces(){Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,"prepareSurfaces");try{// 1. 拿到事务finalTransactiontransaction=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

booleanshowRobustly(SurfaceControl.Transactiont){ProtoLog.i(WM_SHOW_TRANSACTIONS,"SURFACE SHOW (performLayout): %s",title);if(DEBUG_VISIBILITY)Slog.v(TAG,"Showing "+this+" during relayout");if(mSurfaceShown){returntrue;}setShown(true);t.show(mSurfaceControl);returntrue;}

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 12:37:53

内存数据库SQL入门:如何实现高速读写与实时分析

内存数据库 SQL 将数据存储在系统主内存中&#xff0c;通过标准 SQL 接口进行操作&#xff0c;从而极大提升数据访问速度。与传统基于磁盘的数据库相比&#xff0c;它消除了I/O瓶颈&#xff0c;使得实时数据处理和分析成为可能。在当前高并发、低延迟的应用需求下&#xff0c;内…

作者头像 李华
网站建设 2026/6/10 13:06:25

开题报告 springboot和vue_校内闲置书籍交易网站 二手书交易系统

目录 技术选型背景系统核心功能模块技术实现细节创新点与优化方向预期成果 项目技术支持可定制开发之功能亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作 技术选型背景 SpringBoot和Vue是目前主流的前后端分离开发框架组合。SpringBoo…

作者头像 李华
网站建设 2026/6/13 21:17:49

掌控发动机“心脏”精度:蓝光3D扫描在凸轮轴全尺寸检测中的应用

汽车发动机凸轮轴具有连续曲面&#xff08;如凸轮桃升程曲线&#xff09;、复杂集成特征&#xff08;轴颈/相位角/油孔等&#xff09;及严苛公差要求。采用蓝光三维扫描技术&#xff0c;通过非接触全尺寸扫描、秒级动态采集与智能全局拼接&#xff0c;可满足凸轮轴高精度、高效…

作者头像 李华
网站建设 2026/5/16 2:21:30

[信息论与编码理论专题-23]:《信息简史》简介

《信息简史》&#xff08;英文原名&#xff1a;The Information: A History, a Theory, a Flood&#xff09;是美国著名作家、科技史学者詹姆斯格雷克&#xff08;James Gleick&#xff09;于2011年出版的一部广受赞誉的非虚构作品。这本书以“信息”为核心&#xff0c;跨越数千…

作者头像 李华