news 2026/3/12 19:49:43

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/3/10 5:47:45

Silverlight 2中文入门指南:经典技术学习与历史回顾

对于关注微软技术的开发者来说&#xff0c;《Essential Silverlight 2中文版》是一本具有历史意义的入门指南。这本书系统性地介绍了Silverlight 2的核心技术和开发方法&#xff0c;虽然Silverlight技术已经逐步退出主流&#xff0c;但了解它对于理解富互联网应用的发展历程仍有…

作者头像 李华
网站建设 2026/3/10 21:50:04

JS正则验证正数方法:从基础到严格定义

在JavaScript开发中&#xff0c;经常需要对用户输入进行验证&#xff0c;确保其符合预期的数字格式。验证正数是其中常见且基础的需求&#xff0c;合理的正则表达式可以高效、准确地完成这一任务。下面我将从实际应用角度&#xff0c;分享几个关键点。 如何用正则表达式验证正…

作者头像 李华
网站建设 2026/2/27 7:14:13

金融风控平台如何实现Word批注内容到网页的完整迁移?

企业网站后台管理系统富文本编辑器功能扩展开发记录 一、需求分析与技术选型 作为北京某软件公司的前端开发工程师&#xff0c;近期接到客户需求&#xff1a;在企业网站后台管理系统的文章发布模块中增加Word粘贴、Word文档导入以及微信公众号内容粘贴功能。经过详细分析&…

作者头像 李华
网站建设 2026/3/12 4:37:20

Java基于Spring Boot+Vue的户外爱好者网站的设计与实现

所需该项目可以在最下面查看联系方式&#xff0c;为防止迷路可以收藏文章&#xff0c;以防后期找不到 这里写目录标题项目介绍系统实现截图技术栈介绍Spring Boot与Vue结合使用的优势Spring Boot的优点Vue的优点Spring Boot 框架结构解析Vue介绍系统执行流程Java语言介绍系统测…

作者头像 李华