news 2026/5/10 15:40:25

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/5/10 15:39:58

一篇搞定全流程 8个AI论文软件测评:本科生毕业论文+科研写作全攻略

在当前学术研究日益数字化的背景下&#xff0c;本科生在撰写毕业论文和科研文章时&#xff0c;常常面临选题构思困难、文献资料繁杂、格式规范不熟、语言表达不顺等多重挑战。尤其是在AI技术迅速发展的今天&#xff0c;如何高效利用工具提升写作效率成为关键。为此&#xff0c;…

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

vue3中如何实现大文件断点续传的解决方案总结?

一个大三仔的编程血泪史&#xff1a;大文件上传系统开发实录 前言 各位老铁们好&#xff0c;我是广西某不知名大学网络工程专业的大三学生&#xff0c;最近被导师逼着做一个"支持10G文件上传、断点续传、文件夹层级保留、全浏览器兼容、还要加密传输存储"的变态文件…

作者头像 李华
网站建设 2026/5/3 6:23:42

1.4 排序优化实战:从执行计划看懂MySQL的SORT算法内幕

1.4 排序优化实战:从执行计划看懂MySQL的SORT算法内幕 📚 学习目标 通过本节学习,你将掌握: ✅ MySQL排序算法的内部实现机制(内存排序、外部排序) ✅ 如何通过执行计划识别排序性能瓶颈 ✅ 索引排序 vs 文件排序的选择策略 ✅ sort_buffer_size等关键参数的调优方法 …

作者头像 李华
网站建设 2026/5/9 13:21:40

机械设计BOM系统怎样处理Inventor截图到编辑器的矢量转换?

一个C#程序员的UEditorWord导入奇幻漂流&#xff1a;从.NET到Vue的魔幻联动 第一章&#xff1a;需求降临——老板的"简单"需求 "小王啊&#xff0c;咱们后台编辑器得加个Word导入功能&#xff0c;要保留格式和图片啊&#xff01;“老板轻描淡写的一句话&#…

作者头像 李华
网站建设 2026/5/9 1:09:30

vue.js如何支持视频大文件分片上传的方案总结?

【一个被4G大文件逼疯的北京码农自述&#xff1a;如何在信创环境下优雅地让政府文件"飞"起来】 各位战友好&#xff0c;我是老张&#xff0c;北京某软件公司前端组"秃头突击队"队长。最近接了个政府项目&#xff0c;客户要求用国产环境上传4G大文件&#x…

作者头像 李华