文章图片标题

Android性能优化实战-过度绘制的检测以及布局优化

分类:android开发 作者:阿流 评论:0 点击: 3,974 次 发布时间:2018-09-13

版权声明:本文为代码部落原创文章,转载请注明出处。

前言

今天阿流把玩我们公司的APP时,发现打开某个页面会先闪一下,但是在另一台机子情况不会这么明显。review了一下代码并没有发现太大的问题。考虑到两台机子性能差异较大,因此我怀疑是不是UI绘制时耗费了大量的性能,让我们着手验证一下。

过度绘制分析

出问题的这页面如下:

对于UI性能的分析,最简单的方式之一就是使用系统自带的过度绘制功能。步骤如下:

  • 打开开发者选项->调试GPU过度绘制->显示过度绘制区域
  • 开启后会发现界面呈现出各种颜色,在这里每种颜色所表示的含义如下:

最理想的是蓝色,一个像素只绘制一次。合格的页面绘制是白色、蓝色为主,绿色以上区域不能超过整个的三分之一,颜色越浅越好。关于过度绘制,这里不再过多普及,介绍的资料有很多,我们主要在于如何分析及解决。
让我们来看一下打开显示过度绘制之后的页面是怎样的:

我的天,简直是个灾难。- -!

分析布局层次

在这里我们迫不及待的想看看这个看似简单的布局到底有什么故事在里面。一打开布局的XML文件,500多行的代码让我一下子蛋疼了。但是真正的勇士是敢于直面真正的蛋疼。对此我决定用谷歌给我们提供的一款用于布局层次分析的工具HierarchyViewer。对于这个工具的使用,可以参考一下这篇文章。
但是这里我要补充一点的是,这个工具在真机上是没法使用的,只能在虚拟机上使用。这就很蛋疼了,总不能每次我们分析页面都要开一个模拟器来调试吧?好在面包会有的,办法也会有的。我发现了一个可以让真机也可以使用HierarchyViewer的开源库。地址戳这里。
使用方式也很简单,在三个生命周期中各加入三行代码即可:

好了,终于可以打开了,让我们分析一下这个布局的层次:

我去,再一次蛋疼,十几层的布局层次!!而且细看之下嵌套了很多无意义的布局在里面。看来减少布局层次是势在必行了。

解决方案

  1. 使用RelativeLayout代替多层嵌套的LinearLayout

考虑一下下面的布局:

原来的布局最外层使用一个水平LinearLayout嵌套一个imageview,一个垂直Linearyout,再一个TextView。垂直的LinearLayout里再嵌套N个子布局….这样的布局不仅层级较多,而且阅读起来也相对困难。在guide中有这样一段描述:

A RelativeLayout is a very powerful utility for designing a user interface because it can eliminate nested view groups and keep your layout hierarchy flat, which improves performance. If you find yourself using several nested LinearLayout groups, you may be able to replace them with a single RelativeLayout.

大意是说RelativeLayout可以消除嵌套视图并且使布局扁平化,从而提高性能。如果你发现使用的几个嵌套的布局时,推荐使用相对布局来消除嵌套。所以对于上面的例子来说,使用相对布局就可以减少很多不必要的嵌套。代码比较简单我就不举例了。

  1. 使用include标签

观察到上面的布局,发现下面几个item的布局都是一样的,这时候可以使用ListView来代替或者使用include标签来重用相同的布局。例如我们的例子中,就可以先写好每个item的布局,然后在主布局里直接引用进去即可:

item.xml

main.xml

  1. 使用ViewStub标签

在我们的布局中,还有这样一个按钮:

它会根据不同的用户权限来确定是否显示。对于这种情况,完全可以考虑使用ViewStub标签。ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还是不要显示某个布局。我们总结起来无非就是以下几点:

使用ViewStub.inflate()或者ViewStub.setVisibility(View.VISIBLE)来动态加载ViewStub
ViewStub只能Inflate一次,之后会被置空。因此不适用于要反复控制显示隐藏的情况
ViewStub控制的是一个布局,而非某个View

我们来看一下ViewStub的部分源码:

从以上源码我们可以得知下列信息:

调用ViewStub.setVisibility会触发ViewStub.inflate()
ViewStub.inflate()一次之后便会将自身从parent中移除,因此inflate只能调用一次。

针对ViewStub的自身特性以及我们代码中的场景,我们完全可以使用ViewStub来动态加载布局,来提高性能。
代码如下:

我们来看一下最终经过以上优化步奏之后,现在的效果怎么样:

可以看到比最初已经好很多了,剩下的还有一些优化点,这里就不一一举例了。




声明: 本文由( 阿流 )原创编译,转载请保留链接: http://www.daimabuluo.com/blog/android/163.html

Android性能优化实战-过度绘制的检测以及布局优化:等您坐沙发呢!

发表评论