[TOC]
## 什么是 ViewRootImpl
相比 Viewgroup 和 View,ViewRootImpl 可能更为陌生,实际开发中我们基本用不到它。那么
> 什么是 ViewRootImpl 呢?
从结构上来看,ViewRootImpl 和 ViewGroup 其实是一种东西
![](https://img.kancloud.cn/87/5c/875c1faf3ef7ea479e1d969e2ffe6272_1500x400.png)
它们都继承了 ViewParent。ViewParent 是一个接口,定义了一些父 View 的基本行为,比如 requestlayout,getparent 等。不同的是,ViewRootImpl 并不会像 ViewGroup 一样被真正绘制在屏幕上。在 activity 中,它是专门用来绘制 DecorView 的,核心方法是 setView
## “activity,window,View 三者之间的关系是什么?”
我们可以通过一张图来说明。
![](https://img.kancloud.cn/89/25/8925c9c22b24f3461dce89cace67b68c_612x810.png)
如图所示,window 是 activity 里的一个实例变量,本质是一个抽象类,唯一的实现类是 PhoneWindow。
activity 的 setContentView 方法实际上是就是交给 phonewindow 去做的。window 和 View 的关系可以类比为**显示器**和**显示的内容**。
每个 activity 都有一个“显示器”**window**,“显示的内容”就是**DecorView**。这个“显示器”定义了一些方法来决定如何显示内容。比如 setTitleColor setTitle 是设置导航栏的颜色和 title , setAllowReturnTransitionOverlap 设置进/出场动画等等。
所以**window 是 activity 的一个成员变量,window 和 View 是“显示器”和“显示内容”的关系。**
这就是他们的关系
## View 是怎么绘制的
### onCreate
在整个 activity 的生命周期中,setContentView 是在 onCreate 中调用的,它实现了对资源文件的解析,完成了 xml 文件到 View 的转化。期呢?
### onResume 真正开始绘制
他们的关系在源码中一目了然。
![](https://img.kancloud.cn/0d/37/0d37f65f3893b3ed8826bf588fed8ab2_1702x2710.png)
从源码中可以看到,onResume 之后,ActivityThread 通过调用 activity 中 windowmanager 的 addView 方法,将 decorView 传入到 ViewRootImpl 的 setView 方法中,通过 setView 来完成 View 的绘制。
问题又来了,setView 到底有什么魔法,为什么他就能完成 View 的绘制工作呢?
## ViewRootImpl 是如何绘制 View 的
我们再来看一下 setView 方法
![](https://img.kancloud.cn/31/f2/31f20eb0aaae8d0386e1d12a0f36524b_1584x2248.png)
简单来说 setView 做了三件事
① 检查绘制的线程是不是创建 View 的线程。这里可以引申出一个问题,View 的绘制必须在主线程吗?
② 通过同步屏障保证绘制 View 的任务是最优先的
③ 调用 performTraversals 完成 measure,layout,draw 的绘制
看到这里,ViewRootImpl 的绘制基本就完成了。其实这也是面试官希望听到的内容。考察的是面试者对 View 绘制体系的理解。
后续 ViewGroup 和 View 的绘制其实是 performTraversals 对整个 ViewTree 的绘制。他们的关系可以用下面这张图表示
![](https://img.kancloud.cn/c1/1b/c11b40772b9d0b41b5f2d2e3235cabed_1408x940.png)
### mChoreographer
—ViewRoot类的requestLayout()方法
—scheduleTraversals()
—mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)请求刷新信号
—mChoreographer内部handler通过msg机制触发mTraversalRunnable
启动performTraversals();测绘流程
## 为什么我在 onCreate 中调用 View.post
> “不错不错,看来你对 Viewrootimpl 的绘制过程掌握的不错嘛,你刚才提到 View 的绘制是在 onResume 之后才开始的,那为什么我在 onCreate 中调用 View.post 方法可以得到 View 的宽高呢”
这个问题乍看挺唬人的。其实看一眼源码大概就明白了
![](https://img.kancloud.cn/8c/40/8c403e5c38cf6cb52f0957a99ef6da07_1020x610.png)
View.post 会判断当前 View 是否已经被添加到 window 上。如果添加了则立即执行 runnable,如果没有被添加则先放到一个队列中存储起来,等添加到 window 上时再执行。
而 View 被测量完成后才会 attachToWindow。所以当 post 的 runnable 执行时,View 已经绘制完成了。
## MeasureSpec 的理解
View 的大小不仅仅取决于自身的宽高,还取决于父 View 的大小和测量模式。一个 200*200 的父 View 是不可能容纳一个 300*300 的子 View 的,父 View 的 wrap\_content 和 match\_content 也会影响子 View 的大小。
所以 View 的 measure 函数其实应该有 4 个参数:**父 View 的宽**,**父 View 的高**,**宽的测量模式**,**高的测量模式**。
Android 这里用了一个巧妙的设计,用一个 Int 值来表示宽/高的测量模式和大小。一个 int 有 32 位,前 2 位表示测量 MODE,后 30 位表示 SIZE。
为什么要用 2 位表示 MODE 呢?因为 MODE 只有 3 种呀,UNSPECIFIED,EXACTLY,AT_MOST
* 精确模式 EXACTLY:父 View 指定了子 View 确切的大小
* 最大模式 AT_MOST:父 View 指定一个大小,子 View 不能超过这个值
* 未指定模式 UNSPECIFIEND: 父 View 不对子 View 有任何限制
### 我自定义一个 View 的时候,如果不对 MeasureSpec 做处理。使用这个 View 时宽高传入 wrap\_content,结果会怎么样?”
这个考察的就是 View 绘制的实际运用了。当我们自定义一个 View 时,如果继承的是 View,measure 方法走的就是 View 默认的逻辑
![](https://img.kancloud.cn/c7/c9/c7c955b7ce49a425a593f797cd3e4586_1584x1156.png)
所以当我们自定义 View 时,如果没有对 MODE 做处理,设置 wrap\_content 和 match\_content 结果其实是一样的,View 的宽高都是取父 View 的宽高。
## invaliate 和 requestlayout 方法的区别
前面我们说到,ViewRootImpl 作为顶级 View 负责 View 的绘制。所以简单来说,requestlayout 和 invaliate 最终都会向上回溯调用到 ViewRootImpl 的 postTranversals 方法来绘制 View。
不同的是 requestlayout 会绘制 View 的 measure,layout 和 draw 过程。invaliate 因为只添加了绘制 draw 的标志位,只会绘制 draw 过程。
##参考资料
[【面试官爸爸】来给我讲讲View绘制?](https://juejin.cn/post/6979395482946633758)
- Android
- 四大组件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介绍
- MessageQueue详细
- 启动流程
- 系统启动流程
- 应用启动流程
- Activity启动流程
- View
- view绘制
- view事件传递
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大数据
- Binder小结
- Android组件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 迁移与修复
- Sqlite内核
- Sqlite优化v2
- sqlite索引
- sqlite之wal
- sqlite之锁机制
- 网络
- 基础
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP进化图
- HTTP小结
- 实践
- 网络优化
- Json
- ProtoBuffer
- 断点续传
- 性能
- 卡顿
- 卡顿监控
- ANR
- ANR监控
- 内存
- 内存问题与优化
- 图片内存优化
- 线下内存监控
- 线上内存监控
- 启动优化
- 死锁监控
- 崩溃监控
- 包体积优化
- UI渲染优化
- UI常规优化
- I/O监控
- 电量监控
- 第三方框架
- 网络框架
- Volley
- Okhttp
- 网络框架n问
- OkHttp原理N问
- 设计模式
- EventBus
- Rxjava
- 图片
- ImageWoker
- Gilde的优化
- APT
- 依赖注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 协程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 运行期Java-hook技术
- 编译期hook
- ASM
- Transform增量编译
- 运行期Native-hook技术
- 热修复
- 插件化
- AAB
- Shadow
- 虚拟机
- 其他
- UI自动化
- JavaParser
- Android Line
- 编译
- 疑难杂症
- Android11滑动异常
- 方案
- 工业化
- 模块化
- 隐私合规
- 动态化
- 项目管理
- 业务启动优化
- 业务架构设计
- 性能优化case
- 性能优化-排查思路
- 性能优化-现有方案
- 登录
- 搜索
- C++
- NDK入门
- 跨平台
- H5
- Flutter
- Flutter 性能优化
- 数据跨平台