Android性能優化之布局優化

參考《Android性能優化之布局優化》 侵刪

繪制的原理

  1. Android需要把XML布局文件轉換成GPU能夠識別并繪制的對象。這個操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪制到屏幕上的數據信息。
  2. CPU負責把UI組件計算成Polygons,Texture紋理,然后交給GPU進行柵格化渲染。
  3. GPU進行柵格化渲染。
  4. 硬件展示在屏幕上。

為了能夠使得APP流暢,我們需要在每一幀16ms以內完成所有的CPU與GPU計算,繪制,渲染等等操作。也就是幀率為60fps,為什么幀率要為60fps呢,因為人眼與大腦之間的協作無法感知超過60fps的畫面更新。這個流程的表現性能取決于View的復雜程度,View的狀態變化以及渲染管道的執行性能。

Overdraw(過度繪制):描述的是屏幕上的某個像素在同一幀的時間內被繪制了多次。在多層次的UI結構里面,如果不可見的UI也在做繪制的操作,就會導致某些像素區域被繪制了多次,浪費大量的CPU以及GPU資源。(可以通過開發者選項,打開Show GPU Overdraw的選項,觀察UI上的Overdraw情況)。

所以我們需要盡量減少Overdraw。

image

Android布局優化常用方法

綜上,布局的優化其實說白了就是減少層級,越簡單越好,減少overdraw,就能更好的突出性能。
1. 首先是善用相對布局RelativeLayout。布局里面有多個LinearLayout,如果可以使用RelativeLayout進行改變布局,則用RelativeLayout,減少不必要的層級,減少overdraw。這個可以使用Android IDE自帶的工具Hierarchy View去看看布局層級關系。
2. 使用抽象布局標簽include、merge、ViewStub
include標簽常用于將布局中的公共部分提取出來,比如我們要在activity_main.xml中需要上述LinearLayout的數據,那么就可以直接include進去了。

共用的一個comm_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="16dp"
        android:text="這個是MergeLayout"
        android:textSize="16sp" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/comm_layout" />

</RelativeLayout>

merge標簽是作為include標簽的一種輔助擴展來使用,它的主要作用是為了防止在引用布局文件時產生多余的布局嵌套。如上述include標簽引入了之前的LinearLayout之后導致了界面多了一個層級,我們可以通過merge來減少層級,但是布局也沒法正常排布,所以要視布局情況而定。

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:src="@mipmap/ic_launcher"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="16dp"
        android:text="這個是MergeLayout"
        android:textSize="16sp"/>

</merge>

viewstub我們過去在布局里暫時不顯示的布局,都會用Visible.GONE來先隱藏,然后在業務代碼里將其顯示,這樣做的優點是比較靈活,但是缺點是耗費資源,即它仍會被Inflate,被實例化,設置屬性。而viewstub是view的子類,他是一個輕量級View, 隱藏的,沒有尺寸的View。他可以用來在程序運行時簡單的填充布局文件,去電是ViewStub指定的布局被Inflate后,就不能再通過ViewStude來控制它了。如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        android:id="@+id/layout_merge"
        layout="@layout/item_merge_layout" />

    <Button
        android:id="@+id/btn_view_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="顯示ViewStub"
        android:textAllCaps="false"
        android:layout_below="@+id/tv_content"/>

    <Button
        android:id="@+id/btn_view_hide"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="50dp"
        android:layout_toRightOf="@+id/btn_view_show"
        android:text="隱藏ViewStub"
        android:textAllCaps="false"
        android:layout_below="@+id/tv_content"/>

    <ViewStub
        android:id="@+id/vs_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/item_test_linear_layout"
        android:layout_below="@+id/btn_view_show"
        android:layout_marginTop="10dp" />

</RelativeLayout>

使用ViewStub沒那么靈活,限制比較多:
1. ViewStub只能Inflate一次,之后ViewStub對象會被置為空。
2. 某些布局屬性要加在ViewStub而不是實際的布局上面,才會起作用,比如上面用的android:layout_margin*系列屬性,如果加在TextView上面,則不會起作用,需要放在它的ViewStub上面才會起作用。而ViewStub的屬性在inflate()后會都傳給相應的布局。

ConstraintLayout允許你在不適用任何嵌套的情況下創建大型而又復雜的布局。它與RelativeLayout非常相似,所有的view都依賴于兄弟控件和父控件的相對關系。但是,ConstraintLayout比RelativeLayout更加靈活,目前在AndroidStudio中使用也十分方便,就和以前的拖拉控件十分相似。

一些Lint規則如下:
1. 使用組合控件: 包含了一個ImageView以及一個TextView控件的LinearLayout如果能夠作為一個組合控件將會被更有效的處理。
2. 合并作為根節點的幀布局(Framelayout) :如果一個幀布局時布局文件中的根節點,而且它沒有背景圖片或者padding等,更有效的方式是使用merge標簽替換該Framelayout標簽 。
3. 無用的葉子節點:通常來說如果一個布局控件沒有子視圖或者背景圖片,那么該布局控件時可以被移除(由于它處于 invisible狀態)。
4. 無用的父節點 :如果一個父視圖即有子視圖,但沒有兄弟視圖節點,該視圖不是ScrollView控件或者根節點,并且它沒有背景圖片,也是可以被移除的,移除之后,該父視圖的所有子視圖都直接遷移至之前父視圖的布局層次。同樣能夠使解析布局以及布局層次更有效。
5. 過深的布局層次:內嵌過多的布局總是低效率地。考慮使用一些扁平的布局控件,例如 RelativeLayout、GridLayout ,來改善布局過程。默認最大的布局深度為10 。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容