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 打開此工具使用。

相關文章
相關標籤/搜索