性能優(yōu)化——布局優(yōu)化

我們通過對View的源碼分析,其實發(fā)現(xiàn)View的測量和繪制都是遞歸實現(xiàn)的,布局是一個多叉樹的結(jié)構(gòu),多叉樹遍歷所需的時間跟樹的結(jié)構(gòu)是相關(guān)的,布局復(fù)雜那么遍歷View完成測量和繪制的時間就越久,過于復(fù)雜的布局也會影響到應(yīng)用的性能。

1,Hierarchy Viewer的使用

Hierarchy Viewer是Android SDK提供的,用來檢測布局的嵌套和繪制時間的工具,通過Hierarchy Viewer可以直觀的檢測布局的設(shè)計和屬性,從而找到布局中可優(yōu)化點(diǎn)。

下面是一個簡單的布局,用它來說明Hierarchy Viewer的使用方式:

圖1、示例程序運(yùn)行效果

布局如下:
activity_main:

<?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="vertical" >

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:hint="Edit something here" />

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

</LinearLayout>

ok_cancel_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="wrap_content"
    android:orientation="vertical" >

    <Button
        android:id="@+id/ok"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:text="OK" />

    <Button
        android:id="@+id/cancel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="Cancel" />

</LinearLayout>
1,開啟Hierarchy Viewer

AndroidStudio中開啟Hierarchy Viewer步驟是:選擇Tools->Android->Android Device Monitor,在Android Device Monitor中有可能Hierarchy Viewer被隱藏了,按照下圖的步驟就可以打開


圖2、Android Device Monitor開啟Hierarchy Viewer

打開后:

圖3、Hierarchy Viewer的主界面

Windows:顯示當(dāng)前設(shè)備信息,以及當(dāng)前設(shè)備的所有頁面列表。
View Properties:當(dāng)前選中View的屬性。
TreeView:把Activity中所有控件(View)的層次結(jié)構(gòu)從左到右顯示出來,其中最右邊部分是最底層的控件(View)。
Tree Overview:全局概覽,以縮略圖的方式顯示整個應(yīng)用中各控件的層次關(guān)系,并且框出TreeView窗口中顯示部分在全局中的位置,如果一個界面中的控件和層級比較多,可以通過鼠標(biāo)移動這個顯示區(qū)域移動。
Layout View:整體Layout布局圖,以手機(jī)屏幕上真實位置呈現(xiàn)出來,在TreeView中選中某一個控件時,會在Layout View用紅色的框標(biāo)注。

2,使用Hierarchy Viewer查看布局層級與耗時

(1)查看布局層級:在Window窗口點(diǎn)擊選擇需要查看的Activity,然后點(diǎn)擊Load View Hierarchy按鈕即可打開,顯示在TreeView的窗口中,從左到右可以移動鼠標(biāo)進(jìn)行查看。
(2)查看耗時:在菜單欄中單擊Obtain layout times for tree rooted at selected node按鈕


圖4,計算某個View的耗時

這時,就可以在TreeView中的View多了一些信息,截圖如下:

圖5、計算耗時的結(jié)果顯示

圖中灰色方框中信息從上到下分別的意思是,1 view表示這個節(jié)點(diǎn)下只有1個View了,就是自己本身的View,如果某個節(jié)點(diǎn)顯示3 views,那么就表示該節(jié)點(diǎn)下面包括自己,一共有3個元素(自身+2個子view)。下面的時間表示Measure、Layout以及Draw三個階段的耗時。
最后一個框有不同色的三個指示燈,分別對應(yīng)當(dāng)前控件在測量、布局以及畫視圖三個階段,顏色表示這個控件占用的時間百分比,如果是綠色的,表示該控件在該階段比其他50%的控件的速度要快,黃色表示比其他50%的控件的速度要慢,紅色表示該控件在該階段的處理速度是最慢的,就需要注意了。


2,Lint

Android Lint是代碼檢查工具,通過代碼靜態(tài)檢查,可以發(fā)現(xiàn)潛在的代碼問題,并給出優(yōu)化建議。Lint的功能非常強(qiáng)大,可以掃描出我們項目中的代碼的正確性、安全性、性能問題、可用、可達(dá),甚至是國際化問題,并且針對不同的問題給出不同的錯誤級別:
Fatal > Error > Warning > Information > Ignore,我們可以根據(jù)這些錯誤級別來改造代碼,使得代碼更加健壯。
同時Lint還可以用來檢查布局,使用Lint掃描前,先配置需要檢查的項目,只需要檢查Layout層級深度。File——Settings——Inspections——Android Lint

圖6、Android Lint配置.png

如圖所示,在Android Lint的Performance中找到Layout has too many views 和 Layout hierarchy is too deep,勾上,其余的都取消,只要保證Lint就只會拋出與這2個屬性相關(guān)的信息了。其中:
Layout has too many views表示控件太多,默認(rèn)超過80個控件會提示該問題。
Layout hierarchy is too deep表示布局太深,默認(rèn)層級超過10層會提示該問題,可以自定義環(huán)境變量ANDROID_LINT_MAX_DEPTH來修改。布局深度增加會導(dǎo)致內(nèi)存消耗也隨之增加,因此布局盡可能淺而寬。

配置好后,點(diǎn)擊確定。然后返回AndroidStudio中,在菜單欄中選擇Analyze——Inspect Code,然后選擇整個工程或者某個Moudle

圖7、分析布局

為了方便測試,我將上面用到的xml布局中稍作修改,一共嵌套了11層,看看就知道有多恐怖了。

圖8、測試用例嵌套11層

然后我們用Lint工具分析一下代碼,立馬就出了提示:

圖9、Lint提示xml中嵌套過多

Lint里提示到activity_index的布局嵌套超過了10層,提示到如此明顯的地步,那我們就能根據(jù)提示的消息來修改或者優(yōu)化這部分的布局了。


3,布局的優(yōu)化方式

關(guān)于布局的優(yōu)化方式網(wǎng)上有很多的介紹,這里推薦看看郭大神的博客,寫的簡潔明了
Android最佳性能實踐(四)——布局優(yōu)化技巧
總結(jié):
提高布局效率的方法總體來說就是減少層級,提高繪制速度和布局復(fù)用。影響布局效率主要有以下幾點(diǎn):
**
(1)布局的層級越少,加載速度越快。因為View的結(jié)構(gòu)是典型的多叉樹的結(jié)構(gòu),減少布局層級,明顯減少了多叉樹的深度,這樣在遍歷遞歸測量和繪制的時間就相應(yīng)較少很多。
(2)減少同一層級控件的數(shù)量,加載速度會變快。
(3)一個控件的屬性越少,解析越快。刪除控件中的無用屬性也是加快解析速度的一種必要手段。
(4)盡量多使用RelativeLayout或LinearLayout,不要使用絕對布局AbsoluteLayout。同時,在大部分情況下推薦使用RelativeLayout而不是LinearLayout,因為前者在相同情況下,嵌套的層次會少。
(5)將可復(fù)用的組件抽取出來并通過<include/>標(biāo)簽使用。
(6)使用<ViewStub/>標(biāo)簽加載一些不常用的布局。<ViewStub/>修飾的布局不會立刻就加載,而是調(diào)用了inflate方法或者View.VISIBILITY屬性后才加載到內(nèi)存。這種方式特別適合用在無網(wǎng)絡(luò)環(huán)境給頁面做提示的功能上。
(7)使用<merge/>標(biāo)簽減少布局的嵌套層次。
(8)盡可能少用wrap_content,wrap_content會增加布局measure時的計算成本,已知寬高為固定值時,不用wrap_content。
**


4,避免過度繪制

可以參考Android性能優(yōu)化之如何避免Overdraw
Overdraw就是過度繪制,是指在一幀的時間內(nèi)(16.67ms)像素被繪制了多次,理論上一個像素每次只繪制一次是最優(yōu)的,但是由于重疊的布局導(dǎo)致一些像素會被多次繪制,而每次繪制都會對應(yīng)到CPU的一組繪圖命令和GPU的一些操作,當(dāng)這個操作耗時超過16.67ms時,就會出現(xiàn)掉幀現(xiàn)象,也就是我們所說的卡頓,所以對重疊不可見元素的重復(fù)繪制會產(chǎn)生額外的開銷,需要盡量減少Overdraw的發(fā)生。
Android提供了測量Overdraw的選項,在開發(fā)者選項-調(diào)試GPU過度繪制(Show GPU Overdraw),打開選項就可以看到當(dāng)前頁面Overdraw的狀態(tài),就可以觀察屏幕的繪制狀態(tài)。該工具會使用三種不同的顏色繪制屏幕,來指示overdraw發(fā)生在哪里以及程度如何,其中:
沒有顏色: 意味著沒有overdraw。像素只畫了一次。
藍(lán)色: 意味著overdraw 1倍。像素繪制了兩次。大片的藍(lán)色還是可以接受的(若整個窗口是藍(lán)色的,可以擺脫一層)。
綠色: 意味著overdraw 2倍。像素繪制了三次。中等大小的綠色區(qū)域是可以接受的但你應(yīng)該嘗試優(yōu)化、減少它們。
淺紅: 意味著overdraw 3倍。像素繪制了四次,小范圍可以接受。
暗紅: 意味著overdraw 4倍。像素繪制了五次或者更多。這是錯誤的,要修復(fù)它們。

圖10、OverDraw

如何避免過度繪制?
除了上述8點(diǎn)布局優(yōu)化建議外,還有
(1)布局上的優(yōu)化
在XML布局上,如果出現(xiàn)了過度繪制的情況,可以使用Hierarchy View來查看具體的層級情況,可以通過XML布局優(yōu)化來減少層級。需要注意的是,在使用XML文件布局時,會設(shè)置很多背景,如果不是必需的,盡量移除。布局優(yōu)化總結(jié)為以下幾點(diǎn):
移除XML中非必需的背景,或根據(jù)條件設(shè)置。
移除Window默認(rèn)的背景。
按需顯示占位背景圖片。
使用Android自帶的一些主題時,activity往往會被設(shè)置一個默認(rèn)的背景,這個背景由DecorView持有。當(dāng)自定義布局有一個全屏的背景時,比如設(shè)置了這個界面的全屏黑色背景,DecorView的背景此時對我們來說是無用的,但是它會產(chǎn)生一次Overdraw。因此沒有必要的話,也可以移除,代碼如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.getWindow().setBackgroundDrawable(null);
        setContentView(R.layout.activity_index);
    }

針對ListView中的Avatar ImageView的設(shè)置,在getView的代碼中,判斷是否獲取對應(yīng)的Bitmap,獲取Avatar的圖像之后,把ImageView的Background設(shè)置為Transparent,只有當(dāng)圖像沒有獲取到時,才設(shè)置對應(yīng)的Background占位圖片,這樣可以避免因為給Avatar設(shè)置背景圖而導(dǎo)致的過度渲染。

(2)自定義View優(yōu)化
雖然自定義View減少了Layout的層級,但在實際繪制時也是會過度繪制的。原因是有些過于復(fù)雜的自定義View(通常重寫了onDraw方法),Android系統(tǒng)無法檢測在onDraw中具體會執(zhí)行什么操作,無法監(jiān)控并自動優(yōu)化,也就無法避免Overdraw了。但是在自定義View中可以通過canvas.clipRect()來幫助系統(tǒng)識別那些可見的區(qū)域。這個方法可以指定一塊矩形區(qū)域,只有在這個區(qū)域內(nèi)才會被繪制,其他的區(qū)域會被忽視。canvas.clipRect()可以很好地幫助那些有多組重疊組件的自定義View來控制顯示的區(qū)域。clipRect方法還可以幫助節(jié)約CPU與GPU資源,在clipRect區(qū)域之外的繪制指令都不會被執(zhí)行,那些部分內(nèi)容在矩形區(qū)域內(nèi)的組件,仍然會得到繪制,并且可以使用canvas.quickreject()來判斷是否沒和某個矩形相交,從而跳過那些非矩形區(qū)域內(nèi)的繪制操作。


參考資料:
Android最佳性能實踐(四)——布局優(yōu)化技巧
Android性能優(yōu)化之如何避免Overdraw

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容