1.Android渲染知识
①绘制原理
Android系统要求每一帧都要在16ms内绘制完成,平滑的完成一帧意味着任何特殊的帧需要执行所有的渲染代码(包括framework发送给GPU和CPU绘制到缓冲区的命令)都要在16ms内完成,保持流畅的体验。这个速度允许系统在动画和输入事件的过程中以约 60 帧每秒( 1秒 / 0.016帧每秒 = 62.5帧/秒 )的平滑帧率来渲染。
如果应用没有在16ms内完成这一帧的绘制,假设花了24ms来绘制这一帧,那么就会出现掉帧的情况。
系统准备将新的一帧绘制到屏幕上,但是这一帧并没有准备好,所有就不会有绘制操作,画面也就不会刷新。反馈到用户身上,就是用户盯着同一张图看了32ms而不是16ms ,也就是说掉帧发生了。
②掉帧
掉帧是用户体验中一个非常核心的问题。丢弃了当前帧,并且之后不能够延续之前的帧率,这种不连续的间隔会容易引起用户的注意,也就是常说的卡顿、不流畅。引起掉帧的原因非常多,比如:
花了非常多时间重新绘制界面中的大部分东西,这样非常浪费CPU周期;
过度绘制严重,在绘制用户看不到的对象上花费了太多的时间;
有一大堆动画重复了一遍又一遍,消耗 CPU 、 GPU 资源;
频繁的触发垃圾回收;
③为什么是60Fps?
Android系统要求每一帧都要在16ms内绘制完成,那么1秒的帧率就是约60帧每秒( 1秒 / 0.016帧每秒 = 62.5帧/秒 ),那为什么要以 60 Fps来作为App性能的衡量标准呢?这是因为人眼和大脑之间的协作无法感知到超过60Fps的画面更新。
市面上绝大多数Android设备的屏幕刷新频率是60 HZ。当然,超过60 Fps是没有意义的,人眼感知不到区别。24Fps是人眼能感知的连续线性的运动,所以是电影胶圈的常用帧率,因为这个帧率已经足够支撑大部分电影画面所要表达的内容,同时能最大限度地减少费用支出。但是,低于30 Fps是无法顺畅表现绚丽的画面内容的,此时就需要用到60 Fps来达到想要表达的效果。
应用的界面性能目标就是保持60 Fps,这意味着每一帧只有16 ms(1秒 / 60帧率)的时间来处理所有的任务。
④垃圾回收
垃圾回收器是一个在应用运行期间自动释放那些不再引用的内存的机制,常称 GC 。频繁的GC也是导致严重性能问题的罪魁祸首之一。
因为平滑的完成一帧意味着所有渲染代码都必须在16ms内完成。而频繁的GC会严重限制一帧时间内的剩余时间,如果GC所做的工作超过了那些必须的工作,那么留给应用平滑的帧率的时间就越少。越接近16ms ,在垃圾回收事件触发的时候,就越容易导致卡顿。
注意,Android4.4 引进了新的ART虚拟机来取代 Dalvik虚拟机。它们的机制大有不同,简单而言:
(1)Dalvik 虚拟机的GC是非常耗资源的,并且在正常的情况下一个硬件性能不错的Android设备也会很容易耗费掉10 – 20 ms的时间;
(2)ART虚拟机的GC会动态提升垃圾回收的效率,在ART中的中断,通常在2 – 3 ms间。 比Dalvik 虚拟机有很大的性能提升;
ART虚拟机相对于Dalvik虚拟机的垃圾回收来说有一个很大的性能提升,但 2 – 3 ms 的回收时间对于超过16ms帧率的界限也是足够的。因此,尽管垃圾回收在Android 5.0之后不再是耗资源的行为,但也是始终需要尽可能避免的,特别是在执行动画的情况下,可能会导致一些让用户明显感觉的丢帧。
⑤UI 线程
UI线程是应用的主线程,很多的性能和卡顿问题是由于在主线程中做了大量的工作。所以,所有耗资源的操作,比如 IO 操作、网络操作、SQL 操作、列表刷新等,都应该用后台进程去实现,不能占用主线程,主线程是 UI 线程,是保持程序流畅的关键;
在Android 5.0版本里,Android框架层引入了 “ Render Thread ” ,用于向GPU发送实际渲染的操作。这个线程减轻了一些UI线程的操作。但是输入、滚动和动画仍然在UI thread,因为Thread必须能够响应操作。
⑥UI 绘制机制与栅格化
绝大多数渲染操作都依赖两个硬件:CPU、GPU。CPU负责Measure 、layout 、Record 、Execute的计算操作, GPU负责栅格化(Rasterization)操作。非必需的视图组件会带来多余的CPU计算操作,还会占用多余的GPU资源。
栅格化( Rasterization )能将 Button 、 Shape 、 Path 、 Bitmap 等资源组件拆分到不同的像素上进行显示。这个操作很费时,所以引入了 GPU 来加快栅格化的操作。
CPU负责把UI组件计算成多边形( Polygons ),纹理( Texture ),然后交给 GPU 进行栅格化渲染,再将处理结果传到屏幕上显示。
在Android里的那些资源组件的显示(比如 Bitmaps 、 Drawable ),都是一起打包到统一的纹理( Texture )当中,然后再传递到 GPU 里面。
图片的显示,则是先经过CPU的计算加载到内存中,再传给GPU进行渲染。
文字的显示,则是先经过CPU换算成纹理( Texture ),再传给GPU进行渲染,返回到CPU绘制单个字符的时候,再重新引用经过 GPU 渲染的内容。
动画的显示更加复杂,我们需要在 16 ms 内处理完所有 CPU 和 GPU 的计算、绘制、渲染等操作,才能获得应用的流畅体验。



