布局的性能優化之所以重要,因為以下兩個方面:
(1)布局文件是一個xml文件,inflate布局文件其實就是解析xml,根據標簽信息創建相應的布局對象並做關聯。xml中的標簽和屬性設置越多,節點樹的深度越深,在解析時要執行的判斷邏輯、函數的嵌套和遞歸就越多,所以時間消耗越多;
(2)inflate操作只是布局影響的第一個環節,一個界面要顯示出來,在requestLayout后還要執行一系列的measure、layout、draw的操作,每一步的執行時間都會受到布局本身的影響。而界面的最終顯示是所有這些操作完成后才實現的,所以如果布局質量差,會增加每一步操作的時間成本,最終顯示時間就會比較長。
界面性能取決於UI渲染性能. 我們可以理解為UI渲染的整個過程是由CPU和GPU兩個部分協同完成的.
其中, CPU負責UI布局元素的Measure, Layout, Draw等相關運算執行. GPU負責柵格化(rasterization), 將UI元素繪制到屏幕上.
如果我們的UI布局層次太深, 或是自定義控件的onDraw中有復雜運算, CPU的相關運算就可能大於16ms, 導致卡頓.
這個時候, 我們需要借助Hierarchy Viewer這個工具來幫我們分析布局了. Hierarchy Viewer不僅可以以圖形化樹狀結構的形式展示出UI層級, 還對每個節點給出了三個小圓點, 以指示該元素Measure, Layout, Draw的耗時及性能.
那么布局如何優化?總結如下幾點:
1. 遵循一條規則:布局層次盡量少
也就是說,在達到同樣布局效果的前提下,xml文件中樹的深度盡量的潛。要做到這一點需要合理的使用布局控件:
- 典型的情況是你可以使用RelativeLayout來代替LinearLayout實現相同的布局效果;
- 盡量不要嵌套使用RelativeLayout.
- 盡量不要在嵌套的LinearLayout中都使用weight屬性.
- Layout的選擇, 以盡量減少View樹的層級為主.
- 去除不必要的父布局.
- 善用TextView的Drawable減少布局層級
- 如果H Viewer查看層級超過5層, 就需要考慮優化下布局了
- 還有一種是如果布局樹的A節點只有一個子節點B,而B只有一個子節點C,那么B通常是可以去掉的;
- 合理的使用<merge>標簽,如果布局X可以被include到Y中,那么需要考慮X的根節點是否可以設置為<merge>,這樣在解析時會將<merge>的子節點添加到Y中,而<merge>本身不會添加。
- <include> 使用include來重用布局.
ListView優化
- contentView復用
- 引入holder來避免重復的findViewById.
- 分頁加載
2. 使用Lint分析布局
Lint是SDK中tools目錄下的工具,ADT中集成了Lint的可視化控制界面。用Lint掃描應用程序,它會從很多方面對應用進行分析,並提示那些可能有缺陷的地方,其中就包含與性能相關的內容。
打開Activity的布局文件 xxx.xml, 在Android Studio菜單欄中開啟Lint檢查:
選擇當前文件:
會在下方彈出分析結果:
分析結果包括用法檢測(例如版本特有屬性), 國際化(字符串是否提取到strings.xml, Rlt支持等), 以及我們今天的主題---性能分析結果.
點開"Android -> Lint -> Performance"項, 可以看到關於布局性能的建議項. 此例中是說ScrollView的父級LinearLayout是不必要的.
3. 使用HierarchyViewer分析布局
HierarchyViewer(以下簡稱HV)也是SDK中tools目錄下的工具,ADT中也集成了HV的可視化控制界面。可以使用HV查看當前界面的布局,它能提供很多信息,其中有兩個可以幫助我們分析性能問題:
- HV的樹視圖展現了視圖控件的相互關系,可以用來檢查是否有第1點中提到的情況。
- 樹視圖中可以顯示每個節點measure、layout、draw的時間,並且每一項用一個圓點表示其耗時是否正常,每個圓點分別用綠色、黃色、紅色表示耗時正常、警告、危險,這樣就可以很方便的找到有性能瓶頸了。如果樹視圖中沒有顯示這些時間,你可以點擊“Obtain layout times for tree rooted at selected node”按鈕刷新界面顯示。
3.1 啟用H Viewer
比較早接觸Android開發的同學可能知道, H Viewer只能在root過的機器才能使用. 主要是在沒有root過的機器中view server這個服務是沒有開啟的. H Viewer就無法連接到機器獲取view層級信息.
正所謂高手在民間, 大家都嘗試在未root的機器中啟用view server來使用H Viewer. 最具代表性的就是romainguy的ViewServer, 只需集成少量代碼到你的Activity, 相當於在手機端開啟了view server服務,
建立socket通道與PC端的H Viewer通信.
此工程被Android官網吸收, 作為開啟H View的方案之一.
完整開啟H Viewer的方法如下:
- 手機開啟開發者模式, USB調試.
- 根據手機的Android系統版本:
- 4.0及以下, 沒有root. 使用上述的開源工程ViewServer提供的方式.
- 4.0及以下, 已經root. 無需其他額外設置.
- 4.1及以上. 需要在PC端設置ANDROID_HVPROTO環境變量.
設置系統環境變量: ANDROID_HVPROTO, 值為ddm
具體設置系統環境變量根據PC系統不同而異.
做完上述配置后, 你就可以打開H Viewer了, 打開DDMS, 如下操作進入H Viewer界面:
3.2 H Viewer界面詳解
以詳情界面為例說明:

界面分為四個部分:
-
Window
顯示當前連接的設備和供分析的界面. 可手動選擇. -
Tree View
樹狀圖的形式展示該Activity中的View層級結構. 可以放大縮小, 每個節點代表一個View, 點擊可以彈出其屬性, 當前值, 並且在LayoutView中會顯示其在界面中相應位置.
Tree View是我們主要要分析的視圖. -
Tree Overview
Tree View的概覽圖. 有一個選擇框, 可以拖動選擇查看. 選中的部分會在Tree View中顯示. -
Layout View
匹配手機屏幕的視圖, 按照View的實際顯示位置展示出來的框圖.
3.3 H Viewer參數解讀
- 通過Tree View可以很直觀的看到View的層級.
- 點擊Tree View的RepoItemView這個節點:
三個小圓點, 依次表示Measure, Layout, Draw, 可以理解為對應View的onMeasure, onLayout, onDraw三個方法.
- 綠色, 表示該View的此項性能比該View Tree中超過50%的View都要快.
- 黃色, 表示該View的此項性能比該View Tree中超過50%的View都要慢.
- 紅色, 表示該View的此項性能是View Tree中最慢的.
如果界面的Tree View中紅點較多, 那就需要注意了. 一般來說:
1, Measure紅點, 可能是布局中嵌套RelativeLayout, 或是嵌套LinearLayout都使用了weight屬性.
2, Layout紅點, 可能是布局層級太深.
3, Draw紅點, 可能是自定義View的繪制有問題, 復雜計算等.
由上圖, 可以看到我們的RepoItemView的三項指標都不合格, 證明其還有很多優化空間. 層級, 繪制都可以優化.
4. 使用ViewStub延遲加載視圖
ViewStub是一個沒有尺寸大小並且不會在布局中嵌套或渲染任何東西的輕量級的視圖。如果界面中有一部分視圖控件不需要立即顯示,則可以將其寫到一個單獨的layout文件中,用ViewStub標簽代替,當要真正顯示這部分內容時再通過ViewStub將視圖加載進來。
5. 過度繪制(Overdraw)
關於GPU的繪制, 如果我們的界面存在Overdraw, 也可能導致卡頓.
Overdraw: 用來描述一個像素在屏幕上多少次被重繪在一幀上.
通俗的說: 理想情況下, 每屏每幀上, 每個像素點應該只被繪制一次, 如果有多次繪制, 就是Overdraw, 過度繪制了.
5.1 調試Overdraw
Android系統提供了可視化的方案來讓我們很方便的查看overdraw的現象:
在"系統設置"-->"開發者選項"-->"調試GPU過度繪制"中開啟調試:

此時界面可能會有五種顏色標識:
- 原色: 沒有overdraw
- 藍色: 1次overdraw
- 綠色: 2次overdraw
- 粉色: 3次overdraw
- 紅色: 4次及4次以上的overdraw
一般來說, 藍色是可接受的, 是性能優的.
5.2 Overdraw的分析處理
上面有言, 所謂Overdraw, 就是在一個像素點上繪制了多次. 常見的就是:
- 繪制了多重背景.
- 繪制了不可見的UI元素.
Overdraw主要原因是背景的多重繪制, 或是不可見的View在背后繪制等, 但不僅限於此.