前面说到Window才是显示视图(View)的容器,看下图
Android本来就是一个多窗口堆叠的系统,屏幕上有四种颜色,浅蓝,深蓝,绿色,紫色,就代表了四种窗口(Window)
但是上一篇我们也说到了Window---PhoneWindow,PhoneWindow是Window的子类,PhoneWindow对应了是Activity中的一个对象mWindow,是一个不可见的容器,也是一种专门为手机和平板设计的布局方案的体现
那么什么是Window呢?可以这么理解,Window就是一块用来画图的矩形区域
图中虽然有4个window,但最终我们看到的是一幅图,它们的重叠区域都会有序的堆叠,2d屏幕有X轴
和Y轴
,但事实上还有一个与屏幕垂直相交的Z轴
,专业点叫z-order
,Window的摆放顺序就是按照Z坐标的大小,至于他们是通过什么组织在一起的,下面会讲到SurfaceFlinger
说个直白点的例子,dota里的相位鞋,lol里的红叉,都有无视碰撞的效果,就是因为我们看到的一张地图,其实不止一张,它们在Z维度上的大小不同
过度绘制:指的是屏幕上某一个像素点被绘制的次数。到这里,大家应该已经明白为什么会有过度绘制了吧
双缓冲在图像处理上非常重要,它的工作原理是:先把需要呈现的所有元素都画在一张图上(第一层),再把这张图整个投放到屏幕上去(第二层)
双缓冲的优点如下:
前面两点都是铺垫,Surface才是真正的实现,下面来说说Surface里面的MVC
主要用途:Android系统的一个服务,用来生成Surface,管理帧缓冲区,实际做的事儿就是:把不同z坐标的Window按顺序排放,将所有的window合成一张图,也就是一帧。
我们统计FPS是要关注一张完整的图的渲染时间,所以要用到SurfaceFlinger这个服务
adb shell dumpsys SurfaceFlinger --latency + <Component名称>
这条命令,如果指定了正确的Activity名,是会有128行数据结果的,第一行表示刷新间隔,不同手机可能会有不同的值,接下来的127行表示了最近的127帧的渲染情况,每行分三列,第二列比较重要,因为它对应的是这一帧渲染时垂直同步脉冲到来的时间
垂直同步:
这个略复杂,引用几张图来说明
一帧的渲染,我们可以分为以下几个步骤:
上图代表没有垂直同步时的渲染情况,从下往上看,数字代表帧的编号,四个VSync脉冲把时间轴分为了5个部分。
第一部分,先是CPU计算,再GPU处理,这时正在处理第一帧,Display的是第0帧,这里其实就是双缓冲的体现,当第一个VSync脉冲到来前,第1帧处理完毕
第二部分,因为第1帧已经计算完毕,所以可以Display,但是可以看出CPU和GPU的合作工作是非常不自觉的,直到第二部分快结束的时候,才开始第2帧的计算
第三部分,第2帧的计算还没有完成,所以只能继续显示第一帧,这就相当于你盯着一幅图看了32毫秒,这就是掉帧(jank),jank多了人眼就能识别卡顿
再看看加了VSync后的渲染情况,很明显,自觉了很多:
上面我们讲到双缓冲,但,还有更先进的技术,三缓冲,依然是引用几张图来说明:
这是很理想的状态,A显示时计算B,B显示时计算A,一切看起来有条不紊,但,世界很残酷
第一张图是因为每一帧都能在16.67秒之内计算完,但如果不能呢,就会如上图所示,而且又因为VSync的关系,在B慢了之后,A就只能在下下个脉冲开始计算,这样就导致一帧慢,帧帧慢,因此有了----三缓冲
在B慢了之后,A在下下次脉冲加载之前,趁着这个空闲的时间,去计算一下C吧,反正不能让CPU闲着,找个合适的机会,这样在脉冲到来时,可能就已经完成了B和C的计算,她俩都待投放到屏幕,多了个缓冲,解决了一帧慢,帧帧慢的问题
但需要重点说明一下的是:
垂直同步机制是Android一直都有的,三缓冲可不是,因为三缓冲会导致某一帧(比如C)在计算完很久之后才被选中投放到屏幕,即帧延后现象。而且选择C去这个过程本身也是一系列计算,所以三缓冲是选择性开启,当双缓冲造成的jank现象越来越严重,就开启去调节一下
至于利用SurfaceFlinger服务去计算FPS,可以参照上一篇中的那个库,可视化一下,最后的结果,大致长这样:
Android UI呈现这部分就写完了,以后会持续更新其他的专项测试