9、ViewRootImpl 的角色与职责
说实话,ViewRootImpl 这个名字起得有点误导人。我第一次接触时以为它是个 View 的根节点,其实它压根不是 View。它是个 Handler,是个 Binder 客户端,是连接应用层与 WMS 的命门。
我个人习惯把 ViewRootImpl 比作「Android 渲染的发动机」。没有它,你的布局文件就是一纸空文。今天我们就把它拆开看看。
9.1 ViewRootImpl 的创建时机
ViewRootImpl 什么时候出生?答案很简单:Activity 执行 onResume() 之前。
具体流程是这样的:
- ActivityThread 调用 handleResumeActivity()
- 内部调用 WindowManager.addView(decorView, layoutParams)
- WindowManagerImpl 转调 WindowManagerGlobal.addView()
- 在这里 new 出一个 ViewRootImpl
- 调用 root.setView(decorView, params, panelParentView)
嗯,这里要注意:每个 Window 对应一个 ViewRootImpl。Dialog、PopupWindow、Toast,它们都有自己的 ViewRootImpl。我曾在项目中遇到过一个问题——连续弹出多个 Dialog 导致界面卡顿,追查下来就是每个 Dialog 都创建了独立的 ViewRootImpl,在 performTraversals 里互相抢资源。
9.2 与 WindowManagerService 的通信桥梁
ViewRootImpl 内部持有一个 IWindowSession 的 Binder 代理。这个 IWindowSession 是 WMS 给每个应用进程分配的会话通道。
说白了,ViewRootImpl 通过这个通道向 WMS 发送请求:
- addToDisplay():把当前 Window 注册到 WMS
- relayout():请求重新计算窗口尺寸和位置
- remove():销毁窗口
- dispatchInputEvent():传递输入事件
反过来,WMS 也会通过 IWindow 这个回调接口通知 ViewRootImpl:
- 窗口焦点变化
- 配置变更(横竖屏切换)
- Insets 变化(状态栏、导航栏)
我曾经踩过一个坑:在某个定制 ROM 上,WMS 的 relayout 回调迟迟不返回,导致 ViewRootImpl 的 performTraversals 卡死在同步屏障上。最后发现是 WMS 侧的窗口动画锁住了。嗯,这种跨进程的死锁最难查。
9.3 performTraversals() 的核心流程
这是 ViewRootImpl 最核心的方法,没有之一。它负责把 View 树变成屏幕上的像素。我把它称为「三幕剧」:
- 第一幕:测量(Measure) —— 确定每个 View 要多大
- 第二幕:布局(Layout) —— 确定每个 View 在哪
- 第三幕:绘制(Draw) —— 把 View 画出来
但 performTraversals 远不止这三步。它还要处理:
- 窗口尺寸变化(relayoutWindow)
- Surface 创建/销毁
- 输入法可见性
- Insets 分发
- 动画帧回调
下面这张图可以帮你理清整体脉络:
你想想看,每次屏幕刷新(16ms 内),performTraversals 都可能被调用。如果布局嵌套太深,或者 onMeasure 里做了复杂计算,很容易掉帧。我优化过一个案例:一个列表 item 里嵌套了 7 层 LinearLayout,每次测量都要递归好几遍。改成 ConstraintLayout 后,测量时间从 8ms 降到了 1.2ms。
9.4 关键细节:同步屏障与 Choreographer
performTraversals 不是随便乱跑的。它由 Choreographer 驱动。Choreographer 监听 VSYNC 信号,然后通过 同步屏障 优先执行 performTraversals 的消息。
具体来说:
- Choreographer 收到 VSYNC 后,post 一个异步消息到主线程 Handler
- 这个异步消息会越过所有同步消息,优先执行
- ViewRootImpl 的 doTraversal() 被调用
- doTraversal() 内部调用 performTraversals()
为什么要这么设计?因为 渲染必须优先于其他任务。如果用户点击了一个按钮,你总得先把界面画出来,再处理点击逻辑吧?
9.5 总结
ViewRootImpl 是 Android 渲染的枢纽。它:
| 角色 | 职责 |
|---|---|
| 创建者 | 在 Activity 可见之前创建,每个 Window 一个 |
| 通信桥梁 | 通过 IWindowSession 与 WMS 双向通信 |
| 渲染引擎 | performTraversals() 驱动 Measure/Layout/Draw |
| 调度器 | 配合 Choreographer 和同步屏障保证渲染优先级 |
嗯,理解 ViewRootImpl 是理解 Android 渲染的第一步。后面我们讲 WMS 的窗口管理、输入事件分发,都会反复提到它。记住一句话:没有 ViewRootImpl,View 树就是一堆不会画画的空架子。
公众号:蓝海资料掘金营,微信deep3321