我们平常开发中的那些控件,比如Button,TextView,是如何渲染到屏幕上的呢?
简而言之,就是现先将xml解析成相应的对象,然后CPU经过计算以后将图形信息传给GPU,GPU来负责绘制,栅格化等操作,最终显示到手机屏幕上。
Android每16ms对屏幕进行一次刷新,当一帧画面渲染时间超过16ms的时候,垂直同步机制会让显示器硬件等待GPU完成栅格化渲染操作,最后在下一次VSync信号到来时才显示到屏幕上,这样就会让这一帧画面,在屏幕上多停留了16ms
如上图
Step1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成
Step2. 因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧
Step3. 由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理
Step4. 第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。
Step5. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。
所以总的来说,就是屏幕平白无故地多显示了一次第1帧。原因大家应该都看到了,就是CPU没有及时地开始着手处理第2帧的渲染工作,以致“延误军机”。
试想用户盯着同一张图看了32ms而不是16ms,当然很容易察觉出卡顿感,哪怕仅仅出现一次掉帧,用户都会发现动画不是很顺畅。
16 毫秒的时间主要被两件事情所占用
第一件:将 UI 对象转换为一系列多边形和纹理
第二件: CPU 传递处理数据到 GPU 。所以很明显,我们要缩短
这两部分的时间,也就是说需要尽量减少对象转换的次数,以及上传数据的次数
如何减少这两部分的时间 以至于在 16ms 完成呢?
(1)CPU 减少 xml 转换成对象的时间(去掉重复的、减少不必要的布局,尽量扁平化)
(2)GPU 减少重复绘制的时间
GPU的绘制过程,就跟刷墙一样,一层一层地进行,16ms刷一次。这样就会造成,图层覆盖的现象,即无用的图层还被绘制在底层,造成不必要的浪费
GPU过度绘制的几种情况
过度绘制查看工具
在手机端的开发者选项里,里面有个调试GPU过度绘制工具
点击以后会发现你的屏幕变得红红绿绿的,这些颜色就代表了特定部分过度绘制的程度
可以看到,这里的RecyclerView布局列表项重复绘制了四次,导致画面十分卡顿。
通过分析,我们发现问题有以下几个:
其实还有更多方法来减少过度绘制,优化我们的性能。
可以在TextView中加上drawableLeft来设置图片,避免使用imageView+TextView的方式。
因为所有的布局解析都要消耗cpu的计算性能,所以Layout并不是越多越好。
自定义View优化的重点主要在Canvas上,例如出现几个View互相层叠时(卡牌式),可以通过裁剪画布防止过度绘制
在Android Studio 的Android Device Monitor中直接打开这个工具
然后打开我们要检测的app,然后
之后可以看到我们的View树,
看到这个简直崩溃了,太多了。
我们看到视图中几乎所有节点都有三个点,颜色也各不相同
这三个点也是代表着View的Measure, Layout和Draw。
不同颜色意味着不同的速度:
绿: 表示该View的此项性能比该View Tree中超过50%的View都要快;例如,代表Measure的是绿点,意味着这个视图的测量时间快于树中的视图对象的50%。
黄: 表示该View的此项性能比该View Tree中超过50%的View都要慢;
红: 表示该View的此项性能是View Tree中最慢的。
可以很清楚的分辨清不同View的性能以及Measure, Layout和Draw的时间,通过红色的View节点进去查看,布局是否有不合理之处。
优化方式