1. 程式人生 > >Android 效能優化(二)之佈局優化

Android 效能優化(二)之佈局優化

60fps VS 16ms

根據Google官方出品的Android效能優化典範,60幀每秒是目前最合適的影象顯示速度,事實上絕大多數的Android裝置也是按照每秒60幀來重新整理的。為了讓螢幕的重新整理幀率達到60fps,我們需要確保在時間16ms(1000/60Hz)內完成單次重新整理的操作(包括measure、layout以及draw),這也是Android系統每隔16ms就會發出一次VSYNC訊號觸發對UI進行渲染的原因。

V-Sync(垂直同步),早於Voodoo2的時代V-Sync就已引入到DirectX和Windows作業系統當中,其作用主要是讓顯示卡的運算和顯示器重新整理率一致以穩定輸出的畫面質量。

如果整個過程在16ms內順利完成則可以展示出流暢的畫面;然而由於任何原因導致接收到VSYNC訊號的時候無法完成本次重新整理操作,就會產生掉幀的現象,重新整理幀率自然也就跟著下降(假定重新整理幀率由正常的60fps降到30fps,使用者就會明顯感知到卡頓)。

1、Avoid Overdraw

理論上一個畫素每次只繪製一次是最優的,但是由於重疊的佈局導致一些畫素會被多次繪製,Overdraw由此產生。
我們可以通過除錯工具來檢測Overdraw:設定——開發者選項——除錯GPU過度繪製——顯示過度繪製區域。
這裡寫圖片描述
原色 – 沒有過度繪製 – 這部分的畫素點只在螢幕上繪製了一次。
藍色 – 1次過度繪製– 這部分的畫素點只在螢幕上繪製了兩次。
綠色 – 2次過度繪製 – 這部分的畫素點只在螢幕上繪製了三次。
粉色 – 3次過度繪製 – 這部分的畫素點只在螢幕上繪製了四次。
紅色 – 4次過度繪製 – 這部分的畫素點只在螢幕上繪製了五次。

在實際專案中,一般認為藍色即是可以接受的顏色。
多層佈局重複設定了背景色導致Overdraw。
備註:一個容易忽略的點是我們的Activity使用的Theme可能會預設的加上背景色,不需要的情況下可以去掉。
備註:有些過度繪製都是不可避免的,需要結合具體的佈局場景具體分析。

2.減少巢狀層次及控制元件個數

Android的佈局檔案的載入是LayoutInflater利用pull解析方式來解析,然後根據節點名通過反射的方式創建出View物件例項;
同時巢狀子View的位置受父View的影響,類如RelativeLayout、LinearLayout等經常需要measure兩次才能完成,而巢狀、相互巢狀、深層巢狀等的發生會使measure次數呈指數級增長,所費時間呈線性增長;

由此得到結論:那麼隨著控制元件數量越多、佈局巢狀層次越深,展開佈局花費的時間幾乎是線性增長,效能也就越差。
幸運的是,我們有Hierarchy Viewer這個方便視覺化的工具,可以得到:樹形結構總覽、佈局view、每一個View(包含子View)繪製所花費的時間及View總個數。

Hierarchy Viewer使用

Hierarchy Viewer工具提供了一個視覺化介面顯示佈局的層次結構,讓我們可以進行除錯,從而優化介面佈局結構。

選擇Tools > Android > Android Device Monitor
這裡寫圖片描述
進入Android Device Monitor介面,開啟HierarchyViewer
這裡寫圖片描述

裝置連線
如果你是用的模擬器或者開發版手機的話則可以直接進行連線除錯了,如果不是的話,官方提供了兩種方式,進行連線真機除錯:
1、通過第三方庫,安裝和配置ViewServer;
專案中加入ViewServer類,可以在github下載然後把類放在自己專案中;
在manifest檔案申請網路許可權;
在應用中需要除錯的Activity中新增如下程式碼,啟動ViewServer, 這樣就可以與hierarchy viewer通訊了;

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        ViewServer.get(this).addWindow(this);
    }

    public void onDestroy() {
        super.onDestroy();
        ViewServer.get(this).removeWindow(this);
    }

    public void onResume() {
        super.onResume();
        ViewServer.get(this).setFocusedWindow(this);
    }

2、通過設定環境變數
確保手機處於開發者模式;
新增環境變數ANDROID_HVPROTO,值設為ddm。重啟電腦即可生效;
這裡寫圖片描述

分析頁面佈局效能

選擇一個節點,點選右上角的這裡寫圖片描述按鈕,就可以獲取到佈局繪製的時間,如圖:
這裡寫圖片描述
這裡我們主要關注下面的三個圓圈,從左到右依次,代表View的Measure, Layout和Draw的效能,不同顏色代表不同的效能等級:

1) 綠: 表示該View的此項效能比該View Tree中超過50%的View都要快;例如,一個綠點的測量時間意味著這個檢視的測量時間快於樹中的檢視物件的50%。

2)黃: 表示該View的此項效能比該View Tree中超過50%的View都要慢;例如,一個黃點佈局意味著這種觀點有較慢的佈局時間超過50%的樹檢視物件。

3)紅: 表示該View的此項效能是View Tree中最慢的;例如,一個紅點的繪製時間意味著花費時間最多的這一觀點在樹上畫所有的檢視物件。

測量結果分析

紅色節點是代表應用效能慢的一個潛在問題,下面是幾個例子,如何來分析和解釋紅點的出現原因?

1)如果在葉節點或者ViewGroup中,只有極少的子節點,這可能反映出一個問題,應用可能在裝置上執行並不慢,但是你需要指導為什麼這個節點是紅色的,可以藉助Systrace或者Traceview工具,獲取更多額外的資訊;

2)如果一個檢視組裡面有許多的子節點,並且測量階段呈現為紅色,則需要觀察下子節點的繪製情況;

3)如果檢視層級結構中的根檢視,Messure階段為紅色,Layout階段為紅色,Draw階段為黃色,這個是比較常見的,因為這個節點是所有其它檢視的父類;

4)如果檢視結構中的一個葉子節點,有20個檢視是紅色的Draw階段,這是有問題的,需要檢查程式碼裡面的onDraw方法,不應該在那裡呼叫。

佈局常見問題與優化建議

1)沒有用的父佈局時指沒有背景繪製或者沒有大小限制的父佈局,這樣的佈局不會對UI效果產生任何影響。我們可以把沒有用的父佈局,通過標籤合併來減少UI的層次;

2)使用線性佈局LinearLayout排版導致UI層次變深,如果有這類問題,我們就使用相對佈局RelativeLayout代替LinearLayout,減少UI的層次;

3)不常用的UI被設定成GONE,比如異常的錯誤頁面,如果有這類問題,我們需要用標籤,代替GONE提高UI效能。

4)其實大多數繪製時間差異是因為我們線上性佈局中使用了 layout_weight,他會降低對佈局的測量速度,當然這只是你應該謹慎使用佈局權重的原因之一。

5)儘量使用複合圖片,一個線性佈局中如果包含一個 ImageView 和一個 TextView,那麼你可以使用複合圖片來替換

3.Profiling GPU Rendering

開啟裝置的GPU配置渲染工具——》在螢幕上顯示為條形圖,可以協助我們定位UI渲染問題。
這裡寫圖片描述
從Android M版本開始,GPU Profiling工具把渲染操作拆解成如下8個詳細的步驟進行顯示。
這裡寫圖片描述
Swap Buffers:表示處理任務的時間,也可以說是CPU等待GPU完成任務的時間,線條越高,表示GPU做的事情越多;
Command Issue:表示執行任務的時間,這部分主要是Android進行2D渲染顯示列表的時間,為了將內容繪製到螢幕上,Android需要使用Open GL ES的API介面來繪製顯示列表,紅色線條越高表示需要繪製的檢視更多;
Sync & Upload:表示的是準備當前介面上有待繪製的圖片所耗費的時間,為了減少該段區域的執行時間,我們可以減少螢幕上的圖片數量或者是縮小圖片的大小;
Draw:表示測量和繪製檢視列表所需要的時間,藍色線條越高表示每一幀需要更新很多檢視,或者View的onDraw方法中做了耗時操作;
Measure/Layout:表示佈局的onMeasure與onLayout所花費的時間,一旦時間過長,就需要仔細檢查自己的佈局是不是存在嚴重的效能問題;
Animation:表示計算執行動畫所需要花費的時間,包含的動畫有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦這裡的執行時間過長,就需要檢查是不是使用了非官方的動畫工具或者是檢查動畫執行的過程中是不是觸發了讀寫操作等等;
Input Handling:表示系統處理輸入事件所耗費的時間,粗略等於對事件處理方法所執行的時間。一旦執行時間過長,意味著在處理使用者的輸入事件的地方執行了複雜的操作;
Misc Time/Vsync Delay:表示在主執行緒執行了太多的任務,導致UI渲染跟不上vSync的訊號而出現掉幀的情況;

備註:GPU配置渲染工具雖然可以定位出問題發生在某個步驟,但是並不能定位到具體的某一行;當我們定位到某個步驟之後可以使用工具TraceView進行更加詳細的定位。TraceView的使用可以參照《Android效能優化(一)之啟動加速35%》。

佈局優化的通用套路

除錯GPU過度繪製,將Overdraw降低到合理範圍內;
減少巢狀層次及控制元件個數,保持view的樹形結構儘量扁平(使用Hierarchy Viewer可以方便的檢視),同時移除所有不需要渲染的view;
使用GPU配置渲染工具,定位出問題發生在具體哪個步驟,使用TraceView精準定位程式碼;
使用標籤,Merge減少巢狀層次、ViewStub延遲初始化。

經過這幾步的優化之後,一般就不會再有佈局的效能問題,同時還是要強調:優化是一個長期的工作,同時也必須結合具體場景:有取有舍!