一、布局渲染流程

我们平常开发中的那些控件,比如Button,TextView,是如何渲染到屏幕上的呢?

安卓拍照gpu处理 安卓设置gpu渲染程序_Hierarchy Viewer

简而言之,就是现先将xml解析成相应的对象,然后CPU经过计算以后将图形信息传给GPU,GPU来负责绘制,栅格化等操作,最终显示到手机屏幕上。

二、为什么会出现卡顿

安卓拍照gpu处理 安卓设置gpu渲染程序_android UI 性能优化_02

Android每16ms对屏幕进行一次刷新,当一帧画面渲染时间超过16ms的时候,垂直同步机制会让显示器硬件等待GPU完成栅格化渲染操作,最后在下一次VSync信号到来时才显示到屏幕上,这样就会让这一帧画面,在屏幕上多停留了16ms

安卓拍照gpu处理 安卓设置gpu渲染程序_android UI 性能优化_03

如上图
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渲染程序_Hierarchy Viewer_04

GPU过度绘制的几种情况

  1. 自定义控件中,onDraw方法做了过多重复绘制
  2. 布局层次太深,重叠性太强。用户看不到的区域GPU也会渲染,导致耗时增加

过度绘制查看工具
在手机端的开发者选项里,里面有个调试GPU过度绘制工具
点击以后会发现你的屏幕变得红红绿绿的,这些颜色就代表了特定部分过度绘制的程度

  • 蓝色:过度绘制一次(无过度绘制)
  • 淡绿:过度绘制两次
  • 淡红:过度绘制三次
  • 深红:过度绘制四次
    我们优化的目的就是尽量减少红色,看到更多蓝色的区域。

安卓拍照gpu处理 安卓设置gpu渲染程序_Hierarchy Viewer_05

可以看到,这里的RecyclerView布局列表项重复绘制了四次,导致画面十分卡顿。

通过分析,我们发现问题有以下几个:

  1. 背景和主题冲突,重复绘制两次。解决办法,设置主题为null。之后我们发现背景由绿色变为了蓝色,减少了一层绘制
  2. imageView也设置了一个background,导致过度绘制,解决办法,将这个background去掉。之后图片也由淡红变成了绿色。
  3. RecyclerView的item项中多了一层不必要的LinearLayout,将其删除,我们的过度绘制又减少了一层。

其实还有更多方法来减少过度绘制,优化我们的性能。

  • ListView,RecyclerView列表项中避免使用LinearLayout多重嵌套,尽量用一个RelativeLayout。
  • 可以在TextView中加上drawableLeft来设置图片,避免使用imageView+TextView的方式。
    因为所有的布局解析都要消耗cpu的计算性能,所以Layout并不是越多越好。

五、自定义View的优化

自定义View优化的重点主要在Canvas上,例如出现几个View互相层叠时(卡牌式),可以通过裁剪画布防止过度绘制

安卓拍照gpu处理 安卓设置gpu渲染程序_安卓拍照gpu处理_06

六、使用Hierarchy Viewer 优化布局

在Android Studio 的Android Device Monitor中直接打开这个工具

安卓拍照gpu处理 安卓设置gpu渲染程序_android UI 性能优化_07


安卓拍照gpu处理 安卓设置gpu渲染程序_安卓拍照gpu处理_08

然后打开我们要检测的app,然后

安卓拍照gpu处理 安卓设置gpu渲染程序_android性能优化_09

之后可以看到我们的View树,

安卓拍照gpu处理 安卓设置gpu渲染程序_安卓拍照gpu处理_10

看到这个简直崩溃了,太多了。
我们看到视图中几乎所有节点都有三个点,颜色也各不相同
这三个点也是代表着View的Measure, Layout和Draw。
不同颜色意味着不同的速度:

绿: 表示该View的此项性能比该View Tree中超过50%的View都要快;例如,代表Measure的是绿点,意味着这个视图的测量时间快于树中的视图对象的50%。
黄: 表示该View的此项性能比该View Tree中超过50%的View都要慢;
红: 表示该View的此项性能是View Tree中最慢的。

安卓拍照gpu处理 安卓设置gpu渲染程序_Hierarchy Viewer_11

可以很清楚的分辨清不同View的性能以及Measure, Layout和Draw的时间,通过红色的View节点进去查看,布局是否有不合理之处。

优化方式

  • Hierarchy Viewer View树中只有一个子节点且id没有被其他地方使用的布局可以去掉
  • 减少不必要嵌套
  • 使用merge标签避免与父容器重叠。
  • include 复用布局,并且复用的布局可以在GPU里可以高效缓存