根据Google官方出品的Android性能优化典范,60帧每秒是目前最合适的图像显示速度,事实上绝大多数的Android设备也是按照每秒60帧来刷新的。为了让屏幕的刷新帧率达到60fps,我们需要确保在时间16ms(1000/60HZ)内完成单次刷新的操作(包括measure、layout、draw),这也是Android系统每隔16ms就会发出一次VSYNC信号触发对UI进行渲染的原因。
如果整个过程在16ms内顺利完成则可以展示出流畅的画面,然而由于任何原因导致接收到NSYNC信号的时候无法完成本次刷新操作,就会产生掉帧的现象,刷新帧率自然也就跟着下降(假定刷新帧率由正常的60fps降到30fps,用户就会明显感知到卡顿)。
作为开发人员,我们的目标只有一个:保证稳定的帧率来避免卡顿。
理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,Overdraw由此产生。
我们可以通过调试工具来检测Overdraw:设置--开发者选项--调试GPU过渡绘制--显示过渡绘制区域。
原色:没有过渡绘制,这部分的像素点只在屏幕上绘制了一次。
蓝色:1次过渡绘制,这部分的像素点只在屏幕上绘制了两次。
绿色:2次过渡绘制,这部分的像素点只在屏幕上绘制了三次。
粉色:3次过渡绘制,这部分的像素点只在屏幕上绘制了四次。
红色:4次过渡绘制,这部分的像素点只在屏幕上绘制了五次。
在实际项目中,一般认为蓝色即是可以接受的颜色。
我们来看一个简单却隐藏了很多问题的界面,App的设置界面。在没有优化之前打开Overdraw调试,可以看到界面大多数是严重的红色:见下图。
代码如下:
分析布局可知:多层布局重复设置了背景色导致Overdraw。
那么我们结合产品的需求进行下面的优化:
1、去掉每行RelativeLayout的背景色;
2、去掉每行Textview的背景色。
备注:一个容易忽略的点是我们的Activity使用的Theme可能会默认的加上背景色,不需要的情况下可以去掉。
去掉背景色之后再看一下Overdraw:
对比一下优化后的布局的颜色,可以看出Overdraw降到了可以接受的程度。
备注:有些过度绘制都是不可避免的,需要结合具体的布局场景具体分析。
1、Android的布局文件的加载是LayoutInflater利用pull解析方式来解析,然后根据节点名通过反射的方式创建出View对象实例;
2、同时嵌套子View的位置受父View的影响,例如RelativeLayout、LinearLayout等经常需要measure两次才能完成,而嵌套、互相嵌套、深层嵌套等的发生会使measure次数呈指数级增长,所费时间线性增长。
由此得出结论:那么随着控件数量越多、布局嵌套层次越深,展开布局花费的时间几乎是线性增长,性能也就越差。
备注: Hierarchy Viewer不能连接真机的问题可以通过ViewServer这个库解决;
注意:merge标签常用于减少布局嵌套层次,但是只能用于根布局。
include标签和布局性能关系不大,主要用于布局重用,一般和merge标签配合使用。
延迟加载意味着我们可以分批次的将一个Layout中的View解析加载到内存。比如说,我们最先加载Loading的布局,等待网络请求,然后加载常规展示的布局,当网络请求发生错误或者空数据的时候,加载错误布局。
推迟创建对象、延迟初始化,不仅可以提高性能,也可以节省内存(初始化对象不被创建)。Android定义了ViewStub类,ViewStub是轻量级且不可见的视图,它没有大小,没有绘制功能,也不参与measure和layout,资源消耗非常低。
要让ViewStub显示出来,可以用viewStub.infalte()或viewStub.setVisibility(View.VISIBLE)来完成,如下。
ViewStub viewStub = (ViewStub)view.findViewById(R.id.mask);
viewStub.inflate();
现在你可能会问,ViewStub和View.GONE的区别。
他们的共同点是一开始的时候都不会显示,但是View.GONE在布局加载的时候,就已经添加到布局树上了,而ViewStub只会在显示的时候才会渲染布局。最后注意ViewStub加载的布局中不能有merge。
布局优化的通用套路
1、调试GPU过渡绘制,将Overdraw降低到合理范围内;
2、减少嵌套层次及控件个数,保持view的树形结构尽量扁平(使用Hierarchy Viewer可以方便的查看),同时移除所有不需要渲染的view;
3、使用GPU配置渲染工具,定位出问题发生在具体哪个步骤,使用TraceView精准定位代码;
4、使用标签,Merge减少嵌套层次、ViewStub延迟初始化。
经过这几步的优化之后,一般就不会再有布局的性能问题,同时还是要强调:优化是一个长期的工作,同时也必须结合具体场景:有取有舍!