看完上面的流程图,我们很容易想到一个问题,屏幕是以16.6ms的固定频率进行刷新的,但是我们应用层触发绘制的时机是完全随机的(比如我们随时都可以触摸屏幕触发绘制).
如果在GPU
向缓冲区写入数据的同时,屏幕也在向缓冲区读取数据,会发生什么情况呢?
有可能屏幕上就会出现一部分是前一帧的画面,一部分是另一帧的画面,这显然是无法接受的,那怎么解决这个问题呢?
所以,在屏幕刷新中,Android
系统引入了双缓冲机制
GPU
只向Back Buffer
中写入绘制数据,且GPU
会定期交换Back Buffer
和Frame Buffer
,交换的频率也是60次/秒,这就与屏幕的刷新频率保持了同步。
虽然我们引入了双缓冲机制,但是我们知道,当布局比较复杂,或设备性能较差的时候,CPU
并不能保证在16.6ms内就完成绘制数据的计算,所以这里系统又做了一个处理。
当你的应用正在往Back Buffer
中填充数据时,系统会将Back Buffer
锁定。
如果到了GPU
交换两个Buffer
的时间点,你的应用还在往Back Buffer
中填充数据,GPU
会发现Back Buffer
被锁定了,它会放弃这次交换。
这样做的后果就是手机屏幕仍然显示原先的图像,这就是我们常常说的掉帧
布局加载原理
由上面可知,导致掉帧的原因是CPU
无法在16.6ms内完成绘制数据的计算。
而之所以布局加载可能会导致掉帧,正是因为它在主线程上进行了耗时操作,可能导致CPU
无法按时完成数据计算
布局加载主要通过setContentView
来实现,
我们就不在这里贴源码了,一起来看看它的时序图
我们可以看到,在setContentView
中主要有两个耗时操作
-
1.解析
xml
,获取XmlResourceParser
,这是IO过程 -
2.通过
createViewFromTag
,创建View
对象,用到了反射
以上两点就是布局加载可能导致卡顿的原因,也是布局的性能瓶颈
我们如果需要优化布局卡顿问题,首先最重要的就是:确定定量标准
所以我们首先介绍几种获取布局文件加载耗时的方法
常规获取
首先介绍一下常规方法
val start = System.currentTimeMillis()
setContentView(R.layout.activity_layout_optimize)
val inflateTime = System.currentTimeMillis() - start