本文主要是介绍【总结】Android的16ms和垂直同步以及三重缓存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言
手机屏幕是由许多的像素点组成的,每个像素点通过显示不同的颜色最终屏幕呈现各种各样的图像。手机系统的类型和手机硬件的不同导致UI的流畅性体验个不一致。
屏幕展示的颜色数据
- 在GPU中有一块缓冲区叫做 Frame Buffer ,这个帧缓冲区可以认为是存储像素值的二位数组。
- 数组中的每一个值就对应了手机屏幕的像素点需要显示的颜色。
- 由于这个帧缓冲区的数值是在不断变化的,所以只要完成对屏幕的刷新就可以显示不同的图像了.。
- 至于刷新工作手记的逻辑电路会定期的刷新 Frame Buffer的 目前主流的刷新频率为60次/秒 折算出来就是16ms刷新一次。
GPU的Frame Buffer中的数据
- GPU 除了帧缓冲区用以交给手机屏幕进行绘制外. 还有一个缓冲区 Back Buffer 这个用以交给应用的,让CPU往里面填充数据。
- GPU会定期交换 Back Buffer 和 Frame Buffer ,也就是对Back Buffer中的数据进行栅格化后将其转到 Frame Buffer 然后交给屏幕进行显示绘制,同时让原先的Frame Buffer 变成 Back Buffer 让程序处理。
Android的16ms
在Android中我们一般都会提到16ms
绘制一次,那么到底是那里控制这16ms的呢?
在Choreographer
类中我们有一个方法获取屏幕刷新速率:
public final class Choreographer {private static float getRefreshRate() {DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(Display.DEFAULT_DISPLAY);return di.refreshRate;}
}/*** Describes the characteristics of a particular logical display.* @hide*/
public final class DisplayInfo implements Parcelable {/*** The refresh rate of this display in frames per second.* <p>* The value of this field is indeterminate if the logical display is presented on* more than one physical display.* </p>*/public float refreshRate;
}final class VirtualDisplayAdapter extends DisplayAdapter {private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {@Overridepublic DisplayDeviceInfo getDisplayDeviceInfoLocked() {if (mInfo == null) {mInfo = new DisplayDeviceInfo();mInfo.name = mName;mInfo.uniqueId = getUniqueId();mInfo.width = mWidth;mInfo.height = mHeight;mInfo.refreshRate = 60;/***部分代码省略***/}return mInfo;}}
}
一秒60
帧,计算下来大概16.7ms
一帧。
屏幕绘制
作为严重影响 Android
口碑问题之一的UI流畅性差的问题,首先在 Android 4.1
版本中得到了有效处理。其解决方法就是本文要介绍的 Project Butter
。
Project Butter
对 Android Display
系统进行了重构,引入了三个核心元素,即 VSYNC
、Triple Buffer
和 Choreographer
。其中, VSYNC
是理解 Project Buffer
的核心。VSYNC
是Vertical Synchronization(垂直同步)
的缩写,是一种在 PC
上已经很早就广泛使用的技术。 可简单的把它认为是一种定时中断。
接下来,将围绕 VSYNC
来介绍 Android Display
系统的工作方式。请注意,后续讨论将以Display
为基准,将其划分成 16ms
长度的时间段, 在每一时间段中,Display
显示一帧数据(相当于每秒 60
帧)。时间段从 1
开始编号。
没有VSYNC的情况:
由上图可知
- 时间从
0
开始,进入第一个16ms
:Display
显示第0
帧,CPU
处理完第一帧后,GPU
紧接其后处理继续第一帧。三者互不干扰,一切正常。 - 时间进入第二个
16ms
:因为早在上一个16ms
时间内,第1
帧已经由CPU
,GPU
处理完毕。故Display
可以直接显示第1
帧。显示没有问题。但在本16ms
期间,CPU
和GPU
却并未及时去绘制第2
帧数据(注意前面的空白区),而是在本周期快结束时,CPU/GPU
才去处理第2
帧数据。 - 时间进入第
3
个16ms
,此时Display
应该显示第2
帧数据,但由于CPU
和GPU
还没有处理完第2
帧数据,故Display
只能继续显示第一帧的数据,结果使得第1
帧多画了一次(对应时间段上标注了一个Jank
)。 - 通过上述分析可知,此处发生
Jank
的关键问题在于,为何第1
个16ms
段内,CPU/GPU
没有及时处理第2``帧数据?原因很简单,CPU
可能是在忙别的事情(比如某个应用通过sleep
固定时间来实现动画的逐帧显示),不知道该到处理UI绘制
的时间了。可CPU
一旦想起来要去处理第2
帧数据,时间又错过了!
NSYNC的出现
为解决这个问题,Project Buffer
引入了VSYNC
,这类似于时钟中断。结果如图所示:
由图可知,每收到VSYNC
中断,CPU
就开始处理各帧数据。整个过程非常完美。
不过,仔细琢磨图2
却会发现一个新问题:图2
中,CPU
和GPU
处理数据的速度似乎都能在16ms
内完成,而且还有时间空余,也就是说,CPU/GPU
的FPS
(帧率,Frames Per Second
)要高于Display
的FPS
。
确实如此。由于CPU/GPU
只在收到VSYNC
时才开始数据处理,故它们的FPS
被拉低到与Display
的FPS
相同。但这种处理并没有什么问题,因为Android
设备的Display FPS
一般是60
,其对应的显示效果非常平滑。 如果CPU/GPU
的FPS
小于Display
的FPS
,会是什么情况呢?请看下图:
由图可知:
- 在第二个
16ms
时间段,Display
本应显示B
帧,但却因为GPU
还在处理B
帧,导致A
帧被重复显示。 - 同理,在第二个
16ms
时间段内,CPU
无所事事,因为A Buffer
被Display
在使用。B Buffer
被GPU
在使用。注意,一旦过了VSYNC
时间点,CPU
就不能被触发以处理绘制工作了。
三级缓存
为什么CPU
不能在第二个16ms
处开始绘制工作呢?原因就是只有两个Buffer
。如果有第三个Buffer
的存在,CPU
就能直接使用它, 而不至于空闲。出于这一思路就引出了Triple Buffer
。结果如图所示:
由图可知: 第二个16ms
时间段,CPU
使用C Buffer
绘图。虽然还是会多显示A帧
一次,但后续显示就比较顺畅了。
是不是Buffer
越多越好呢?回答是否定的。由图4
可知,在第二个时间段内,CPU
绘制的第C
帧数据要到第四个16ms
才能显示, 这比双Buffer
情况多了16ms
延迟。所以,Buffer
最好还是两个,三个足矣。
以上对VSYNC
进行了理论分析,其实也引出了Project Buffer
的三个关键点: 核心关键:需要VSYNC
定时中断。 Triple Buffer
:当双Buffer
不够使用时,该系统可分配第三块Buffer
。 另外,还有一个非常隐秘的关键点:即将绘制工作都统一到VSYNC
时间点上。这就是Choreographer
的作用。在它的统一指挥下,应用的绘制工作都将变得井井有条。
转自MrlLeed的: Android垂直同步和三重缓存
如果有对源码有兴趣的话可以继续阅读另一篇文章:Android系统的编舞者Choreographer
文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦~!
这篇关于【总结】Android的16ms和垂直同步以及三重缓存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!