布局优化的核心思想是优化布局嵌套层级(层级越少,View绘制时越快)
一、Android系统屏幕UI刷新机制
首先需要明白一个概念,如果我们想要屏幕流畅的运行,就必须保证UI全部的测量、布局和绘制的时间在16ms内 为什么是16ms? 因为人眼与大脑之间的协作无法感知超过60fps的画面更新,而16ms也就是每秒刷新60fps 16ms=1000/60Hz,也就是说超过16ms用户就会感知到卡顿.
熟悉屏幕UI刷新机制,首先需要了解下刷新率和帧率:
刷新率(Refresh Rate): 指屏幕在一秒内刷新屏幕的次数,例如60HZ
帧率(Frame Rate): 指GPU在一秒内操作画面的帧数,例如30fps,60fps
在一个典型的显示系统中,一般包括CPU、GPU、display(可以理解为屏幕或显示器)三个部分,CPU负责计算数据,把计算好数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到buffer里存起来,然后display负责把buffer里的数据呈现到屏幕上.
显示过程简单的说就是 CPU/GPU准备好数据,存入buffer,display每隔一段时间去buffer里取数据,然后显示出来.但是刷新频率和帧率并不是总能保持相同的节奏.
针对上述情况,Android系统中引入了VSYNC的机制(Vertical Synchronization 垂直同步,我们可以理解为帧同步),
它为了保证GPU生成帧的速度和display刷新的速度保持一致,Android系统会每16ms就会发出一次VSYNC信号触发UI渲染更新.
VSYNC最重要的作用是防止出现画面撕裂(screen tearing).所谓画面撕裂 是指一个画面上出现了两帧画面的内容(如下图).
为什么会出现这种情况呢?这种情况一般是因为显卡输出帧的速度高于显示器的刷新速度,导致显示器并不能及时处理输出的帧,
而最终出现了多个帧的画面都留在了显示器上的问题.
画面撕裂也就是帧率超过刷新率情况(图一):
我们看下图一,在没有VSync的情况下屏幕刷新第二帧画面的时候 GPU生成了2 3两个画面 导致画面撕裂现象,如果引入VSYNC机制后,要求绘制只能在收到VSYNC信号之后才能进行,因此杜绝了GPU一直不停的进行绘制,帧的生成速度高于屏幕的刷新速度,导致生成的帧不能被正常显示,只能丢弃,这样就出现了丢帧的情况
其实android设备更多的情况是帧率小于刷新频率情况(图二):
我们看下图二,GPU生成帧的速度从60fps突然掉到60fps以下,就会出现某些帧显示的画面内容就会与上一帧的画面相同.这样一来,用户在两个16ms看到的是同一帧画面,因此会给用户卡顿不流畅的感受. 图一: 更多的情况是GPU生成的帧率小于刷新频率情况(图二):
帧率从60fps突然掉到60fps以下,就会出现某些帧显示的画面内容就会与上一帧的画面相同.因此给用户卡顿不流畅的感受.
二、布局的选择
首先FrameLayout能实现的优先使用FrameLayout,因为Framelayout是最简单高效的ViewGroup(为什么它是最高效的呢?最简单的办法就是我们可以通过查看它源码的行数,FrameLayout,源代码行数是最少的,代码逻辑也是最简单的)
其次优先选择RelativeLayout,因为RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局(等下还会介绍一个ConstraintLayout,它可以在不嵌套布局的情况下更简单的完成更多复杂的布局)
最后是当在RelativeLayout和LinearLayout在不嵌套情况下同时能够满足需求时,优先选择LinearLayout,因为RelativeLayout功能相对复杂,同时会有重复绘制的情况
什么是重复绘制?
重复测量视图并不一定是因为错误。RelativeLayout就需要经常对它的子视图测量两次,以确
保所有子视图被放置在了正确的位置。如果 LinearLayout 的子视图设置了 layout weight属
性,那么 LinearLayou也需要测量两次以确定子视图的确切尺寸。如果是嵌套的LinearLayout
或者是RelativeLayout,测量的次数会呈指数增长(两层嵌套会进行4次测量,3层嵌套会进行
8 次测量,等等)。
- 避免过度绘制(Overdraw)
Overdraw: 指屏幕上某一个像素点在同一帧的时间内被绘制了多次.
在多层次的UI结构中,如果不可见的UI也在做绘制的操作,就会导致某些像素区域被绘制了多次,浪费大量的CPU以及GPU资源,我们可以在手机的设置—->开发者选项—->打开"调试GPU过度绘制" 查看Overdraw过度绘制情况.
如图 通过4种颜色展示不同程度的Overdraw的情况: