Android性能优化之布局优化篇

怎样才能写出优秀的Android App,是每个程序员追求的目标。那么怎么才能写出一个优秀的App呢?相信不少初学者也会有这种迷茫。一句话来回答这个问题:细节很重要。今天咱们就从最基础的XML布局来谈谈怎么提升Android性能问题吧!css

也许你常常会遇到比较复杂的布局,这种状况下,最简单的方法就是多层嵌套实现效果,可是最简单的方法是不是最优的方法呢? 这里须要打一个大大的问号?????经验告诉咱们,每每简单的方法,获得的结果不是最优解,那么咱们经过一个例子来研究一下怎么去优化咱们的XML布局吧,下面经过经典微信中的“发现”tab页面中的布局来看看怎么实现。html

这里写图片描述

上面这张图片是微信界面截图,看到这张效果图的第一眼会让开发者想到使用线性布局实现这种左边图片,右边文字,一行白色背景效果很方便。那么咱们就按照通常思路写出以下布局代码:android

?程序员

1微信

2app

3ide

4工具

5布局

6性能

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

<code class="hljs" xml=""><linearlayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" tools:context=".MainActivity" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color_eeeeee">

 

    <linearlayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1">

 

 

        <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:layout_margintop="20dp" android:gravity="center_vertical" android:clickable="true">

 

            <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/afe">

 

            <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android :color/black" android:text="@string/fiends" android:layout_marginleft="15dp">

        </textview></imageview></linearlayout>

 

 

        <linearlayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:layout_margintop="20dp">

 

            <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:gravity="center_vertical" android:clickable="true">

 

                <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/afg">

 

                <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android:color/black" android:text="@string/scan" android:layout_marginleft="15dp">

            </textview></imageview></linearlayout>

 

            <view android:layout_width="match_parent" android:layout_height="0.5dp" android:background="@color/color_e0e0e0" android:layout_marginleft="10dp" android:layout_marginright="10dp"></view>

 

            <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:gravity="center_vertical" android:clickable="true">

 

                <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/afh">

 

                <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android:color/black" android:text="@string/shake" android:layout_marginleft="15dp">

            </textview></imageview></linearlayout>

 

        </linearlayout>

 

        <linearlayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:layout_margintop="20dp">

 

 

            <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:gravity="center_vertical" android:clickable="true">

 

                <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/afd">

 

                <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android:color/black" android:text="@string/nearby" android:layout_marginleft="15dp">

            </textview></imageview></linearlayout>

 

            <view android:layout_width="match_parent" android:layout_height="0.5dp" android:background="@color/color_e0e0e0" android:layout_marginleft="10dp" android:layout_marginright="10dp"></view>

 

            <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:gravity="center_vertical" android:clickable="true">

 

                <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/afb">

 

                <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android:color/black" android:text="@string/float_bottle" android:layout_marginleft="15dp">

            </textview></imageview></linearlayout>

 

        </linearlayout>

 

 

        <linearlayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:layout_margintop="20dp">

 

            <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:gravity="center_vertical" android:clickable="true">

 

                <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/agg">

 

                <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android:color/black" android:text="@string/shopping" android:layout_marginleft="15dp">

            </textview></imageview></linearlayout>

 

            <view android:layout_width="match_parent" android:layout_height="0.5dp" android:background="@color/color_e0e0e0" android:layout_marginleft="10dp" android:layout_marginright="10dp"></view>

 

            <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/item_bg_select" android:paddingright="20dp" android:paddingleft="20dp" android:gravity="center_vertical" android:clickable="true">

 

                <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ak6">

 

                <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="16sp" android:textcolor="@android:color/black" android:text="@string/games" android:layout_marginleft="15dp">

            </textview></imageview></linearlayout>

 

        </linearlayout>

    </linearlayout>

 

    <linearlayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white">

 

        <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/weixin" android:drawabletop="@drawable/ala">

 

        <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/countans" android:drawabletop="@drawable/al9">

 

        <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/finds" android:drawabletop="@drawable/alc">

 

        <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/me" android:drawabletop="@drawable/ale">

 

    </textview></textview></textview></textview></linearlayout>

 

 

</linearlayout>

</code>

以上布局的效果图以下:

这里写图片描述

是否是差很少实现了微信同样的效果?那么咱们怎么来判断以上布局是否是最优的呢?固然,咱们是有工具来查看的。相信不少童鞋用过了,第一个就是 Hierarchy View,第二个就是 显示GPU过分绘制。< 喎�"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="hierarchy-view检测布局嵌套层次">Hierarchy View检测布局嵌套层次

若是你是使用AS开发的话,你能够在 AS 工具栏中点击 Tools–>Android–>Android Device Monitor–>Hierarchy View。(至于Hierarchy View怎么使用这里就不仔细介绍了)你能够经过这个工具来查看当前布局的层次结构,以下图的布局的层次结构就是上面微信的布局:

这里写图片描述

ContentFrameLayout接点以后就是咱们上面XML代码的布局了,从上图能够看到,咱们布局最多有 5 层,其实你从代码中也能够看到是 5 层,那么咱们是否能减小以上的布局的嵌套层次呢?答案是确定的,废话很少说,咱们直接上一份我优化过的布局代码吧。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<code class="hljs" perl=""><relativelayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" tools:context=".MainActivity" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color_eeeeee" xmlns:app="http://schemas.android.com/apk/res-auto">

 

 

    <textview android:id="@+id/tv_one" android:background="@drawable/item_bg_select" android:layout_margintop="20dp" android:text="@string/fiends" android:drawableleft="@drawable/afe">

 

    

 

        <textview android:background="@drawable/item_bg_select" android:text="@string/scan" android:drawableleft="@drawable/afg">

 

        <textview android:background="@drawable/item_bg_select" android:text="@string/shake" android:drawableleft="@drawable/afh">

 

    </textview></textview></android.support.v7.widget.linearlayoutcompat>

 

    

 

        <textview android:background="@drawable/item_bg_select" android:text="@string/nearby" android:drawableleft="@drawable/afd">

 

        <textview android:background="@drawable/item_bg_select" android:text="@string/float_bottle" android:drawableleft="@drawable/afb">

    </textview></textview></android.support.v7.widget.linearlayoutcompat>

 

 

    

 

        <textview android:background="@drawable/item_bg_select" android:text="@string/shopping" android:drawableleft="@drawable/agg">

 

        <textview android:background="@drawable/item_bg_select" android:text="@string/games" android:drawableleft="@drawable/ak6">

    </textview></textview></android.support.v7.widget.linearlayoutcompat>

 

 

    <include android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/bottom_layout" android:layout_alignparentbottom="true">

</include></textview></relativelayout>

</code>

哇,代码量少了不少啊,代码也简洁了许多,让人看着就很舒服,那么咱们到底进行了怎样的优化呢?从如下几点总结:

使用 style 主题来定义一个通用的属性,从而重复利用代码,减小代码量。上面代码使用了两个style,一个是textStyle 和 LinerLayoutStyle ,代码以下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<code class="hljs" applescript=""><!--TextView 的通用属性--><style type="text/css" name="textStyle"><item name=android:layout_width>match_parent</item>

        <item name=android:layout_height>wrap_content</item>

        <item name=android:textSize>16sp</item>

        <item name=android:textColor>@android:color/black</item>

        <item name=android:paddingRight>20dp</item>

        <item name=android:paddingLeft>20dp</item>

        <item name=android:gravity>center_vertical</item>

        <item name=android:clickable>true</item></style>

 

    <!--LinerLayout 的通用属性--><style type="text/css" name="LinerLayoutStyle"><item name=android:layout_width>match_parent</item>

        <item name=android:layout_height>wrap_content</item>

        <item name=android:layout_marginTop>20dp</item>

        <item name=android:background>@android:color/white</item>

        <item name=android:orientation>vertical</item></style></code>

2.减小布局嵌套的层次,上面布局使用TextView能够设置四个方向图片来直接替代LinerLayout下包裹一个ImageView 和TextView。从而这里减小了一层嵌套布局,再次利用RelativeLayout相对布局又减小了一层桥套,提升了加载布局的效率。看图:

这里写图片描述

从图中看出,不只减小了两层嵌套布局,并且组件数目也减小,从而减小布局绘制的时间,大大提升了布局加载效率。

3.使用 LinearLayoutCompat 组件来实现线性布局元素之间的分割线,从而减小了使用View来实现分割线效果。LinearLayoutCompat的具体内容请参考 http://blog.csdn.net/feiduclear_up/article/details/46619637 。

4.使用 include 标签加载底部菜单栏布局,include 标签的目的是重复利用布局,来减小代码了。

?

1

2

3

4

5

6

7

8

9

10

11

12

<code class="hljs" xml=""><!--?xml version=1.0 encoding=utf-8?-->

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white">

 

    <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/weixin" android:drawabletop="@drawable/ala">

 

    <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/countans" android:drawabletop="@drawable/al9">

 

    <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/finds" android:drawabletop="@drawable/alc">

 

    <textview android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:textcolor="@color/color_9e9e9e" android:text="@string/me" android:drawabletop="@drawable/ale">

 

</textview></textview></textview></textview></linearlayout></code>

显示GPU过分绘制

你能够在手机打开 设置—->开发者选项—->显示GPU过分绘制,这个开关的做用是按不一样颜色值来显示布局的过分绘制,绘制的层次从最优到最差:蓝,绿,淡红,红。给出一张直观的形象图片来表示吧

这里写图片描述

图片从上到下表明不一样层次的OverDraw,咱们在布局时候,尽可能减小红色 Overdraw,看到更多的蓝色区域。来看看微信的 Overdraw图和咱们自定义的布局Overdraw图吧

腾讯微信Overdraw图
咱们自定义的布局

第一张图是 腾讯微信的Overdraw 图,第二张图片是咱们自定义的 Overdraw 图片。从上面两张图片看出,咱们本身的布局和微信原版的布局 Overdraw 过分绘制状况差很少,没啥区别。那么咱们能不能去减小红色部分的过分绘制呢?试试吧!

咱们先去掉最顶端布局RelativeLayout的背景

?

1

<code class="hljs" perl="">android:background=@color/color_eeeeee</code>

而后修改每一个item选择资源 selector

?

1

<code class="hljs" perl=""> android:background=@drawable/item_bg_select1</code>

以前的 item_bg_select.xml 资源是以下代码:

?

1

2

3

4

5

6

7

8

<code class="hljs" xml=""><!--?xml version=1.0 encoding=utf-8?-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">

 

    <item android:state_pressed="true" android:drawable="@color/color_e0e0e0"></item>

 

    <item android:drawable="@android:color/white"></item>

 

</selector></code>

修改以后的 item_bg_select1.xml资源代码以下:

?

1

2

3

4

5

6

<code class="hljs" xml=""><!--?xml version=1.0 encoding=utf-8?-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">

 

    <item android:state_pressed="true" android:drawable="@color/color_e0e0e0"></item>

 

</selector></code>

咱们发现,新的 selector资源去除了

?

1

<code class="hljs" xml=""><item android:drawable="@android:color/white"></item></code>

由于整个背景是白色的,无需重复设置正常状况下item的背景颜色。

修改以后的效果图以下:

这里写图片描述

看出,基本没有红色区域,从而提升布局的绘制效率。

总结:如今看来,咱们经过减小背景颜色的设置来减小Overdraw的状况。咱们本身布局过分绘制的状况比微信自己的状况有很大的改善,是否是感受很nice~~。

懒加载布局 ViewStub

除了以上两种方法来优化布局,还有其余办法来继续优化布局,在某些状况下,有些布局是仅在须要时才加载,好比小米手机的添加联系人功能就有在编辑姓名的时候有一个下拉按钮显示更多输入信息,看图

这里写图片描述
这里写图片描述

遇到这种状况,咱们首先想到的 就是将不经常使用的元素使用INVISIBLE或者GONE进行隐藏,这样是否真的好呢?是否达到了 布局优化的最终效果呢?利用 INVISIBLE只是隐藏布局,可是布局仍是占居当前位置,且系统在加载布局的时候这一部分仍是会绘制出来,一样花费绘制时间。那么有没有好的办法来解决这一问题呢?不言而喻,咱们可使用懒加载布局 ViewStub。

ViewStub是Android为此提供了一种很是轻量级的控件。ViewStub虽然说也是View的一种,可是它没有大小,没有绘制功能,也不参与布局,资源消耗很是低,将它放置在布局当中基本能够认为是彻底不会影响性能的。

下面咱们来学习一下 ViewStub的使用方法吧!

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<code class="hljs" xml=""><!--?xml version=1.0 encoding=utf-8?-->

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp">

 

 

    <edittext android:id="@+id/et_name" android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/name" android:drawableright="@drawable/a0e">

 

    <viewstub android:id="@+id/view_stub" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout="@layout/item_name">

 

    <edittext android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/comp">

 

    <edittext android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/lead">

 

 

</edittext></edittext></viewstub></edittext></linearlayout></code>

item_name.xml 布局以下:

?

1

2

3

4

5

6

7

8

9

10

11

12

<code class="hljs" xml=""><!--?xml version=1.0 encoding=utf-8?-->

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">

 

    <edittext android:id="@+id/et_name1" android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/name1">

 

    <edittext android:id="@+id/et_name2" android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/name2">

 

    <edittext android:id="@+id/et_name3" android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/name3">

 

    <edittext android:id="@+id/et_name4" android:layout_width="150dp" android:layout_height="wrap_content" android:hint="@string/name4">

 

</edittext></edittext></edittext></edittext></linearlayout></code>

而后你在代码中这么使用便可

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<code class="hljs" avrasm="">findViewById(R.id.et_name).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);

                if (null != viewStub) {

                    //主要是这一句显示更多布局

                    View view = viewStub.inflate();

                    EditText name1 = (EditText) view.findViewById(R.id.et_name1);

                    EditText name2 = (EditText) view.findViewById(R.id.et_name2);

                    EditText name3 = (EditText) view.findViewById(R.id.et_name3);

                    EditText name4 = (EditText) view.findViewById(R.id.et_name4);

                }

            }

        });</code>

效果图以下:
这里写图片描述
这里写图片描述

从效果图能够看出,当用户点击姓名下拉按钮时,其余关于姓名的布局就加载出来了,并且原来的布局是显示在ViewStub布局之下的,位置显示没有任何异常。固然你能够经过 setVisibility(View.VISIBLE)或者viewStub.inflate()方来来让其显示,经过setVisibility(View.INVISIBLE)来隐藏 ViewStub。

Android Lint 工具

这里不展开介绍 Lint 工具,感兴趣的童鞋能够本身网上搜索一把,这个工具主要是用来检查工程中代码的不合理,布局不合理,资源重复,图片重复,等,让开发者进一步优化本身的应用。若是你是AS 用户,你能够在工具栏 Analyze—>Inspect Code 打开此工具使用。

相关文章
相关标签/搜索