Android 性能优化(二)之布局优化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yy471101598/article/details/78794362

60fps VS 16ms

根据Google官方出品的Android性能优化典范,60帧每秒是目前最合适的图像显示速度,事实上绝大多数的Android设备也是按照每秒60帧来刷新的。为了让屏幕的刷新帧率达到60fps,我们需要确保在时间16ms(1000/60Hz)内完成单次刷新的操作(包括measure、layout以及draw),这也是Android系统每隔16ms就会发出一次VSYNC信号触发对UI进行渲染的原因。

V-Sync(垂直同步),早于Voodoo2的时代V-Sync就已引入到DirectX和Windows操作系统当中,其作用主要是让显卡的运算和显示器刷新率一致以稳定输出的画面质量。

如果整个过程在16ms内顺利完成则可以展示出流畅的画面;然而由于任何原因导致接收到VSYNC信号的时候无法完成本次刷新操作,就会产生掉帧的现象,刷新帧率自然也就跟着下降(假定刷新帧率由正常的60fps降到30fps,用户就会明显感知到卡顿)。

1、Avoid Overdraw

理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,Overdraw由此产生。
我们可以通过调试工具来检测Overdraw:设置——开发者选项——调试GPU过度绘制——显示过度绘制区域。
这里写图片描述
原色 – 没有过度绘制 – 这部分的像素点只在屏幕上绘制了一次。
蓝色 – 1次过度绘制– 这部分的像素点只在屏幕上绘制了两次。
绿色 – 2次过度绘制 – 这部分的像素点只在屏幕上绘制了三次。
粉色 – 3次过度绘制 – 这部分的像素点只在屏幕上绘制了四次。
红色 – 4次过度绘制 – 这部分的像素点只在屏幕上绘制了五次。

在实际项目中,一般认为蓝色即是可以接受的颜色。
多层布局重复设置了背景色导致Overdraw。
备注:一个容易忽略的点是我们的Activity使用的Theme可能会默认的加上背景色,不需要的情况下可以去掉。
备注:有些过度绘制都是不可避免的,需要结合具体的布局场景具体分析。

2.减少嵌套层次及控件个数

Android的布局文件的加载是LayoutInflater利用pull解析方式来解析,然后根据节点名通过反射的方式创建出View对象实例;
同时嵌套子View的位置受父View的影响,类如RelativeLayout、LinearLayout等经常需要measure两次才能完成,而嵌套、相互嵌套、深层嵌套等的发生会使measure次数呈指数级增长,所费时间呈线性增长;

由此得到结论:那么随着控件数量越多、布局嵌套层次越深,展开布局花费的时间几乎是线性增长,性能也就越差。
幸运的是,我们有Hierarchy Viewer这个方便可视化的工具,可以得到:树形结构总览、布局view、每一个View(包含子View)绘制所花费的时间及View总个数。

Hierarchy Viewer使用

Hierarchy Viewer工具提供了一个可视化界面显示布局的层次结构,让我们可以进行调试,从而优化界面布局结构。

选择Tools > Android > Android Device Monitor
这里写图片描述
进入Android Device Monitor界面,打开HierarchyViewer
这里写图片描述

设备连接
如果你是用的模拟器或者开发版手机的话则可以直接进行连接调试了,如果不是的话,官方提供了两种方式,进行连接真机调试:
1、通过第三方库,安装和配置ViewServer;
项目中加入ViewServer类,可以在github下载然后把类放在自己项目中;
在manifest文件申请网络权限;
在应用中需要调试的Activity中添加如下代码,启动ViewServer, 这样就可以与hierarchy viewer通信了;

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        ViewServer.get(this).addWindow(this);
    }

    public void onDestroy() {
        super.onDestroy();
        ViewServer.get(this).removeWindow(this);
    }

    public void onResume() {
        super.onResume();
        ViewServer.get(this).setFocusedWindow(this);
    }

2、通过设置环境变量
确保手机处于开发者模式;
新增环境变量ANDROID_HVPROTO,值设为ddm。重启电脑即可生效;
这里写图片描述

扫描二维码关注公众号,回复: 5752958 查看本文章

分析页面布局性能

选择一个节点,点击右上角的这里写图片描述按钮,就可以获取到布局绘制的时间,如图:
这里写图片描述
这里我们主要关注下面的三个圆圈,从左到右依次,代表View的Measure, Layout和Draw的性能,不同颜色代表不同的性能等级:

1) 绿: 表示该View的此项性能比该View Tree中超过50%的View都要快;例如,一个绿点的测量时间意味着这个视图的测量时间快于树中的视图对象的50%。

2)黄: 表示该View的此项性能比该View Tree中超过50%的View都要慢;例如,一个黄点布局意味着这种观点有较慢的布局时间超过50%的树视图对象。

3)红: 表示该View的此项性能是View Tree中最慢的;例如,一个红点的绘制时间意味着花费时间最多的这一观点在树上画所有的视图对象。

测量结果分析

红色节点是代表应用性能慢的一个潜在问题,下面是几个例子,如何来分析和解释红点的出现原因?

1)如果在叶节点或者ViewGroup中,只有极少的子节点,这可能反映出一个问题,应用可能在设备上运行并不慢,但是你需要指导为什么这个节点是红色的,可以借助Systrace或者Traceview工具,获取更多额外的信息;

2)如果一个视图组里面有许多的子节点,并且测量阶段呈现为红色,则需要观察下子节点的绘制情况;

3)如果视图层级结构中的根视图,Messure阶段为红色,Layout阶段为红色,Draw阶段为黄色,这个是比较常见的,因为这个节点是所有其它视图的父类;

4)如果视图结构中的一个叶子节点,有20个视图是红色的Draw阶段,这是有问题的,需要检查代码里面的onDraw方法,不应该在那里调用。

布局常见问题与优化建议

1)没有用的父布局时指没有背景绘制或者没有大小限制的父布局,这样的布局不会对UI效果产生任何影响。我们可以把没有用的父布局,通过标签合并来减少UI的层次;

2)使用线性布局LinearLayout排版导致UI层次变深,如果有这类问题,我们就使用相对布局RelativeLayout代替LinearLayout,减少UI的层次;

3)不常用的UI被设置成GONE,比如异常的错误页面,如果有这类问题,我们需要用标签,代替GONE提高UI性能。

4)其实大多数绘制时间差异是因为我们在线性布局中使用了 layout_weight,他会降低对布局的测量速度,当然这只是你应该谨慎使用布局权重的原因之一。

5)尽量使用复合图片,一个线性布局中如果包含一个 ImageView 和一个 TextView,那么你可以使用复合图片来替换

3.Profiling GPU Rendering

打开设备的GPU配置渲染工具——》在屏幕上显示为条形图,可以协助我们定位UI渲染问题。
这里写图片描述
从Android M版本开始,GPU Profiling工具把渲染操作拆解成如下8个详细的步骤进行显示。
这里写图片描述
Swap Buffers:表示处理任务的时间,也可以说是CPU等待GPU完成任务的时间,线条越高,表示GPU做的事情越多;
Command Issue:表示执行任务的时间,这部分主要是Android进行2D渲染显示列表的时间,为了将内容绘制到屏幕上,Android需要使用Open GL ES的API接口来绘制显示列表,红色线条越高表示需要绘制的视图更多;
Sync & Upload:表示的是准备当前界面上有待绘制的图片所耗费的时间,为了减少该段区域的执行时间,我们可以减少屏幕上的图片数量或者是缩小图片的大小;
Draw:表示测量和绘制视图列表所需要的时间,蓝色线条越高表示每一帧需要更新很多视图,或者View的onDraw方法中做了耗时操作;
Measure/Layout:表示布局的onMeasure与onLayout所花费的时间,一旦时间过长,就需要仔细检查自己的布局是不是存在严重的性能问题;
Animation:表示计算执行动画所需要花费的时间,包含的动画有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦这里的执行时间过长,就需要检查是不是使用了非官方的动画工具或者是检查动画执行的过程中是不是触发了读写操作等等;
Input Handling:表示系统处理输入事件所耗费的时间,粗略等于对事件处理方法所执行的时间。一旦执行时间过长,意味着在处理用户的输入事件的地方执行了复杂的操作;
Misc Time/Vsync Delay:表示在主线程执行了太多的任务,导致UI渲染跟不上vSync的信号而出现掉帧的情况;

备注:GPU配置渲染工具虽然可以定位出问题发生在某个步骤,但是并不能定位到具体的某一行;当我们定位到某个步骤之后可以使用工具TraceView进行更加详细的定位。TraceView的使用可以参照《Android性能优化(一)之启动加速35%》。

布局优化的通用套路

调试GPU过度绘制,将Overdraw降低到合理范围内;
减少嵌套层次及控件个数,保持view的树形结构尽量扁平(使用Hierarchy Viewer可以方便的查看),同时移除所有不需要渲染的view;
使用GPU配置渲染工具,定位出问题发生在具体哪个步骤,使用TraceView精准定位代码;
使用标签,Merge减少嵌套层次、ViewStub延迟初始化。

经过这几步的优化之后,一般就不会再有布局的性能问题,同时还是要强调:优化是一个长期的工作,同时也必须结合具体场景:有取有舍!

猜你喜欢

转载自blog.csdn.net/yy471101598/article/details/78794362