找出Android卡顿的元凶——渲染性能优化

一个 Android 应用是否流畅,或者说是否存在卡顿、丢帧现象,都与 60fps 和 16ms 有关 。那么这两个值是怎么来的呢?为什么以这两个值为衡量标准呢?本文主要讨论下渲染性能方面决定 Android 应用流畅性的因素 。

  • 12fps(帧/秒)
由于人类眼睛的特殊生理结构,如果所看画面之帧率高于每秒约 10 - 12fps 的时候,就会认为是连贯的 。早期的无声电影的帧率介于 16 - 24fps 之间,虽然帧率足以让人感觉到运动,但往往被认为是在快放幻灯片 。在 1920 年代中后期,无声电影的帧率提高到 20 - 26fps 之间 。
  • 24fps
1926 年有声电影推出,人耳对音频的变化更敏感,反而削弱了人对电影帧率的关注 。因为许多无声电影使用 20 - 26fps 播放,所以选择了中间值 24fps 作为有声电影的帧率 。之后 24fps 成为 35mm 有声电影的标准 。
  • 30fps
早期的高动态电子游戏,帧率少于每秒 30fps 的话就会显得不连贯 。这是因为没有动态模糊使流畅度降低 。(注:如果需要了解动态模糊技术相关知识,可以查阅 这里)
  • 60fps
在实际体验中,60fps 相对于 30fps 有着更好的体验 。
  • 85fps
一般而言,大脑处理视频的极限 。
所以,总体而言,帧率越高体验越好 。一般的电影拍摄及播放帧率均为每秒 24 帧,但是据称《霍比特人:意外旅程》是第一部以每秒 48 帧拍摄及播放的电影,观众认为其逼真度得到了显著的提示 。
目前,大多数显示器根据其设定按 30Hz、 60Hz、 120Hz 或者 144Hz 的频率进行刷新 。而其中最常见的刷新频率是 60Hz 。
这样做是为了继承以前电视机刷新频率为 60Hz 的设定 。而 60Hz 是美国交流电的频率,电视机如果匹配交流电的刷新频率就可以有效的预防屏幕中出现滚动条,即互调失真 。
16 ms正如上面所述目前大多数显示器的刷新率是 60Hz,Android 设备的刷新率也是 60Hz 。只有当画面达到 60fps 时 App 应用才不会让用户感觉到卡顿 。那么 60fps 也就意味着 1000ms/60Hz = 16ms 。也就是说 16ms 渲染一次画面才不会卡顿 。
CPU vs GPU渲染操作通常依赖于两个核心组件:CPU 与 GPU 。CPU 负责包括 Measure、Layout、Record、Execute 的计算操作,GPU 负责 Rasterization (栅格化)操作 。
CPU 通常存在的问题的原因是存在非必需的视图组件,它不仅仅会带来重复的计算操作,而且还会占用额外的 GPU 资源 。
找出Android卡顿的元凶——渲染性能优化

文章插图
 
CPU vs GPUAndroid UI 与 GPU
了解 Android 是如何利用 GPU 进行画面渲染有助于我们更好的理解性能问题 。
那么一个最实际的问题是:Activity 的画面是如何绘制到屏幕上的?那些复杂的 XML 布局文件又是如何能够被识别并绘制出来的?
找出Android卡顿的元凶——渲染性能优化

文章插图
 
Resterization 栅格化是绘制那些 Button、Shape、Path、String、Bitmap 等组件最基础的操作 。它把那些组件拆分到不同的像素上进行显示 。
这是一个很费时的操作,GPU 的引入就是为了加快栅格化的操作 。
CPU 负责把 UI 组件计算成 Polygons,Texture 纹理,然后交给 GPU 进行栅格化渲染 。
找出Android卡顿的元凶——渲染性能优化

文章插图
 
CPU 与 GPU 工作流程然而,每次从 CPU 转移到 GPU 是一件很麻烦的事情,所幸的是 OpenGL ES 可以把那些需要渲染的纹理缓存在 GPU Memory 里面,在下次需要渲染的时候可以直接使用 。但是,如果你更新了 GPU 缓存的纹理内容,那么之前保存的状态就丢失了 。
在 Android 里面那些由主题所提供的资源(例如:Bitmaps、Drawables)都是一起打包到统一的 Texture 纹理当中,然后再传递到GPU里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的 。
当然,随着 UI 组件的越来越丰富,有了更多演变的形态 。例如,显示图片的时候,需要先经过 CPU 的计算加载到内存中,然后传递给 GPU 进行渲染 。文字的显示更加复杂,需要先经过 CPU 换算成纹理,然后再交给 GPU 进行渲染,回到 CPU 绘制单个字符的时候,再重新引用经过 GPU 渲染的内容 。动画则是一个更加复杂的操作流程 。
为了能够使得 App 流畅,我们需要在每一帧 16ms 以内处理完所有的 CPU 与 GPU 计算,绘制,渲染等等操作 。
UI 组件的更新通常来说,Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象 。这个操作是在 DisplayList 的帮助下完成的 。DisplayList 持有所有将要交给 GPU 绘制到屏幕上的数据信息 。


推荐阅读