安卓App帧率优化的一些经验总结

1、背景:安卓的渲染机制

  我们需要了解Android系统是如何处理UI组件的更新操作的,主要包含以下4个步骤:

  (1)Android需要把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。

  (2)CPU负责把UI组件计算成Polygons,Texture纹理,然后交给GPU进行栅格化渲染。

  (3)GPU进行“栅格化(转化为像素)”渲染。

  (4)硬件展示在屏幕上。

  每次从CPU转移到GPU是一件很麻烦的事情,所幸的是OpenGL ES可以把那些需要渲染的文理Hold在GPU的Memory里面,在下次需要渲染的时候直接进行操作。Android里面那些由主题所提供的资源,例如Bitmap,Drawables都是一起打包到统一的Texture文理当中, 然后再传递到GPU里面,这意味着每次你需要使用这些资源的时候,都是直接从文理里面进行获取渲染的。当然随着UI组件的越来越丰富,有了更多演变的形态。例如显示图片的时候,需要先经过CPU的计算加载到内存中,然后传递到GPU进行渲染。文字的显示更加复杂,需要先经过CPU换算成纹理,然后再交给GPU进行渲染,回到CPU绘制单个字符的时候,再重新引用经过GPU渲染的内容。动画则是一个更加复杂的流程。

 

2、app使用过程中,不流畅的原因

  为了能够使得APP流畅,我们需要在每一帧16ms以内完成所有的CPU与GPU计算,绘制,渲染等等操作。也就是帧率为60fps,为什么帧率要为60fps呢,因为人眼与大脑之间的协作无法感知超过60fps的画面更新。开发app的性能目标就是保持60fps,这意味着每一帧你只有16ms=1000/60的时间来处理所有的任务。这里需要了解下刷新率和帧率:

  Refresh Rate:代表了屏幕在一秒内刷新屏幕的次数,这取决于硬件的固定参数,例如60HZ。

  Frame Rate:代表了GPU在一秒内挥之操作的帧数,例如30fps,60fps。

  此外这里引入了VSYNC(垂直同步信号量)的机制( Android4.1引入了Vsync,用来同步渲染,让AppUI和SurfaceFlinger可以按硬件产生的VSync节奏进行工作),Android就是通过VSYNC信号来同步UI绘制和动画,使得它们可以获得一个达到60fps的固定的帧率。GPU会获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,他们两者不停地进行协作。不幸的是,刷新频率和帧率并不是总能保持相同的节奏。如果发生帧率与刷新频率不一致的情况,就会容易出现显示内容发生断裂,重叠。

  帧率超过刷新率只是理想的状况,在超过60fps的情况下,GPU所产生的帧数据会因为等待VSYNC的刷新信息而被Hold住,这样能够保持每次刷新都有实际的新的数据可以显示。

  当某个View需要被渲染时,如果在一帧的时间内(16.67ms)像素被绘制了多次(理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制),而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这个操作耗时超过16.67ms时,就会出现掉帧现象,也就是我们所说的卡顿。

  所以,app不流畅的首要原因是:“过度绘制”。

 

3、处理“过度绘制”

  Android提供了测量Overdraw的选项,在开发者选项-调试GPU过度绘制(Show GPU Overdraw),打开选项就可以看到当前页面Overdraw的状态,就可以观察屏幕的绘制状态。该工具会使用三种不同的颜色绘制屏幕,来指示overdraw发生在哪里以及程度如何,其中:
  没有颜色: 意味着没有overdraw。像素只画了一次。
  蓝色: 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。
  绿色: 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。
  浅红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。
  暗红: 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的,要修复它们。

安卓App帧率优化的一些经验总结_第1张图片 调试GPU过度绘制​​​​

 

(1)利用Android Lint工具寻求可能优化布局的层次  

  一些Lint规则如下:

  <1>使用组合控件: 包含了一个ImageView以及一个TextView控件的LinearLayout如果能够作为一个组合控件将会被更有效的处理。

  <2>合并作为根节点的帧布局(Framelayout) :如果一个帧布局时布局文件中的根节点,而且它没有背景图片或者padding等,更有效的方式是使用merge标签替换该Framelayout标签。

  <3>无用的叶子节点:通常来说如果一个布局控件没有子视图或者背景图片,那么该布局控件时可以被移除(由于它处于 invisible状态)。

  <4>无用的父节点 :如果一个父视图即有子视图,但没有兄弟视图节点,该视图不是ScrollView控件或者根节点,并且它没有背景图片,也是可以被移除的,移除之后,该父视图的所有子视图都直接迁移至之前父视图的布局层次。同样能够使解析布局以及布局层次更有效。

  <5>过深的布局层次:内嵌过多的布局总是低效率地。考虑使用一些扁平的布局控件,例如 RelativeLayout、GridLayout ,来改善布局过程。默认最大的布局深度为10。

(2)合理选择控件容器

  Android提供的Layout控件主要包括LinearLayout、TableLayout、FrameLayout、RelativeLayout。俗话说条条大路通罗马,同一个界面我们可以使用不同的容器控件来表达,但是各个容器控件描述界面的复杂度是不一样的。一般来说LinearLayout最易,RelativeLayout较复杂。但是尺有所短,寸有所长,LinearLayout只能用来描述一个方向上连续排列的控件,而RelativeLayout几乎可以用于描述任意复杂度的界面。但是我又要说但是了,表达能力越强的容器控件,性能往往略低一些,因为系统需要将更多的时间花在计算子控件的位置上。综上所述:LinearLayout易用,效率高,表达能力有限。RelativeLayout复杂,表达能力强,效率稍逊。

(3)去掉window的默认背景

  当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色,那么DecorView的background此时对我们来说是无用的,但是它会产生一次Overdraw,带来绘制性能损耗。

  具体方法:

  <1>在onCreate()中setContentView()之后调用  getWindow().setBackgroundDrawable(null);  

  <2>在theme中添加  android:windowbackground="null";

(4)使用抽象布局标签include、merge、ViewStub

  <1>include标签常用于将布局中的公共部分提取出来

  <2>merge标签是作为include标签的一种辅助扩展来使用,它的主要作用是为了防止在引用布局文件时产生多余的布局嵌套,从而可以较少一层布局。

  具体操作方法:把父view的Layout控件,替换成merge标签即可。

  <3>ViewStub标签可以理解为“高优占位符”

  我们经常会遇到这样的情况,运行时动态根据条件来决定显示哪个View或布局。常用的做法是把View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。

  这时,推荐的做法是使用android.view.ViewStub,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还是不要显示某个布局。

(5)多使用“9patch”

(6)减少Alpha转化

  假如对一个View做Alpha转化,需要先将View绘制出来,然后做Alpha转化,最后将转换后的效果绘制在界面上。通俗点说,做Alpha转化就需要对当前View绘制两遍,可想而知,绘制效率会大打折扣,耗时会翻倍,所以Alpha尽量少用。

(7)使用最新的布局方式ConstaintLayout(约束布局)

  具体使用方法和优势对比,见:https://mp.weixin.qq.com/s/gGR2itbY7hh9fo61SxaMQQ?

 

 

 

 

 

你可能感兴趣的:(安卓,性能优化,帧率优化)