type
status
date
slug
summary
tags
category
password
icon
View 绘制流程
View 绘制流程函数调⽤



从上图可知,当前
Activity#onResume()
时仍然不可获取到 Activity 内部的View 的宽高,因为真正测量的实际发生一个异步方法中,这个异步方法的调用会发出在主线程的 Looper 下一次循环中,而添加这个异步方法发生在上图 ViewRootImpl#scheduleTraversals()
内
从上图可知
View 的测量至少经过两次,当测量完成时才会调用
performLayout
,并回调 dispatchOnGlobalLayout
所以在其中能够获取到 测量后的 View 的尺寸。而通过 View.post{ /* 获取view宽高 */}
是发送在下一帧的 dispatchMessage
方法中的;当测量布局完成后,会将
mLayoutRequest
成员变量设置为 Flase
,当外部再次调用 performTraversals
方法时,就可以跳过测量和布局,直接开始绘制;在 ViewRootImpl 的 draw 方法中会根据是否开启了硬件加速来决定是采用
DecorView.draw
的软件绘制,还是 ThreadedRender.draw
的硬件绘制Activity 和 Dialog 的最底层的 View 是什么?
DecorView
view.post()
传入的 Runnable 一定会被执行吗?
看情况,如果 view 被 remove 了那么就没机会执行了。
饿了么团队有一个开源的 Debug 工具 UETool 在这个工具中,它可以自由的获取 Dialog 的根布局,从而获得 Dialog 中所有 View 的信息。但是我们知道,Dialog 不像 Activity 一样在创建时有回调使用(Application#registerActivityLifecycle),那么从哪里可以非侵入获取到根布局呢(指的是不需要修改原本的 Dialog 继承自指定基类)(提示因为 Debug 工具,不考虑特殊情况,可以使用反射。
答案:
反射 WindowManagerGlobal
在每个 Android 应用中,都存在一个唯一的 WindowManagerGlobal 对象,这个对象中包含了能和 WMS 进行双向 Binder 通信的通道

这三个集合,保存了应用中所有顶层 View 相关信息。
- mViews:保存的是所有顶层View的对象,(实际上是 DecorView 对象)。
- mRoots:保存的是和顶层 View 关联的 ViewRootImpl 对象。
- mParams: 保存的是创建顶层 View 的 layout 参数;
在⼦线程中更新 UI 不报错的⼏种⽅式
- 主线程申请成功后⼦线程申请
(补充解释: textview.text = "Main" 这⼀⾏代码会在主线程运⾏,这会在主线程正常的执⾏ requsetLayout 的整个流程,这样就完成了「申请」修改布局。此时,在⼦线程⽴即调⽤ textview.text = "xx.." 这个代码就会因为它已经「申请」过requestLayout 了,就不会层层往上调⽤ parent 的 requsetLayout() ⽅法,也就不会在⼦线程触发 checkThread() ⽅法了!)

- 在⼦线程中创建 ViewRootImpl
- 利⽤硬件加速机制绕开
requestLayout()
在硬件加速的⽀持下,如果控件只是进⾏了 invalidate() ,⽽没有触发 requestLayout() 是不会触发 ViewRootImpl#checkThread() 的。
- SurfaceView
Android 中有⼀个控件 SurfaceView ,它可以通过 holder 获得 Canvas 对象,可以直接在⼦线程中更新 UI。
- 作者:shuouyang
- 链接:https://notion-tree.vercel.app/article/3b001a2b-37ce-4e84-b391-37de9f0c1a77
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。