Android性能優(yōu)化之布局優(yōu)化

參考《Android性能優(yōu)化之布局優(yōu)化》 侵刪

繪制的原理

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

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

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

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

image

Android布局優(yōu)化常用方法

綜上,布局的優(yōu)化其實說白了就是減少層級,越簡單越好,減少overdraw,就能更好的突出性能。
1. 首先是善用相對布局RelativeLayout。布局里面有多個LinearLayout,如果可以使用RelativeLayout進行改變布局,則用RelativeLayout,減少不必要的層級,減少overdraw。這個可以使用Android IDE自帶的工具Hierarchy View去看看布局層級關(guān)系。
2. 使用抽象布局標(biāo)簽include、merge、ViewStub
include標(biāo)簽常用于將布局中的公共部分提取出來,比如我們要在activity_main.xml中需要上述LinearLayout的數(shù)據(jù),那么就可以直接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標(biāo)簽是作為include標(biāo)簽的一種輔助擴展來使用,它的主要作用是為了防止在引用布局文件時產(chǎn)生多余的布局嵌套。如上述include標(biāo)簽引入了之前的LinearLayout之后導(dǎo)致了界面多了一個層級,我們可以通過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來先隱藏,然后在業(yè)務(wù)代碼里將其顯示,這樣做的優(yōu)點是比較靈活,但是缺點是耗費資源,即它仍會被Inflate,被實例化,設(shè)置屬性。而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()后會都傳給相應(yīng)的布局。

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容