Android 之 GPU過度繪製與圖形渲染優化

暴打小女孩發表於2016-08-27

前言

本文主要對過度繪製和圖形渲染做一個概念性的描述。

同時以案例方式列出一些簡單適用的優化措施。

如果你已對過度繪製有過一些瞭解,那麼你應該明白,僅是簡單的層級優化對過度繪製的改善是很小的。所以,這時候你可以參考這篇文章:

優化Android過度繪製

另外如果你還想知道更多關於View優化原理,可以參考 Google 釋出的 Android 效能優化典範

概念

GPU過度繪製

是指在一個畫素點上繪製多次(超過一次)。舉一個簡單的例子:顯示一個什麼都沒有做的activity介面算作畫了1層,給activity加一個背景是第2層,在上面放了一個Text View(有背景的Text View)是第3層,Text View顯示文字就是第4層 。

僅僅只是為了顯示一個文字,卻在同一個畫素點繪製了四次,這是一定要優化的!

還有,過度繪製對動畫效能的影響是極其嚴重的。如果你想要流暢的動畫效果,那麼一定不能忽視過度繪製!!

圖形渲染優化

一個View的繪製過程:測量、佈局、畫圖。三者的累積時間,就是一個View的最終繪製時間 。 過多的層級、無用的子節點父節點、過於依賴系統計算位置的佈局屬性(如: weight)。都會引起上述三個過程時間的增加。

關鍵點/字

  • 過渡繪製優化與圖形渲染優化都其目的都是為了提供一個高效的UI。其目的相似,優化方式也有相同之處,所以一起進行總結。
  • 除錯GPU過渡繪製顏色區域說明
    • 無/白色:繪製1次
    • 藍色:繪製2次(理想狀態)
    • 綠色:繪製3次
    • 淺紅:繪製4次(要優化了)
    • 深紅:繪製5次或5次以上。(必須要優化了)

 

6631820931164660027

    • 除錯Hierarchy Viewer 顏色說明下方三個原點從左到右:測量、佈局、畫圖時間
      • 紅色:該View所用時間超過大部分View很多
      • 黃色:該View所用時間超過大部分View
      • 綠色:該View所用時間低於大部分View

20151016211507297

  • 引起過度繪製的兩個主要因素:層級與背景圖片
    • 層級為透明時(不新增背景),不會引起過度繪製,但會引起測量、佈局、畫圖時間的顯著提高。
    • 改變View形狀,也算是繪製一層。新增一個橢圓形的黑色背景,算作兩層
    • 值得注意的是,背景圖片的繪製是及其耗時的
  • 一個通常的錯誤觀念就是使用基本的佈局結構(例如:LinearLayout、FrameLayout等)能夠在大多數情況下產生高效率的佈局。
    • 淺層佈局效率高於深層佈局
    • 佈局巢狀層數相同情況效率對比:LinearLayout ≈ FrameLayout > RelativeLayout
    • 基本的線性佈局會導致過於累贅的層級巢狀結構。使用相對佈局優化。
    • 但並不是所有情況下都應該用相對佈局。(相對佈局過於複雜,且通讀性差)應考慮權衡關係。

優化措施

  • 在Theme中給activity增加背景。使用WindowBackground屬性。背景的繪製是非常耗時的,在Theme中新增背景,不算繪製一層,並且View渲染時間減少很多。
  • 減少層級,沒必要的背景圖如果一個View和它所在的Layout的顏色相同,就不需要給兩個都設定背景
  • 避免使佈局太深,而應該讓佈局更淺更深用相對佈局替換線性佈局
  • 無用的子節點、父節點刪除沒有免費的午餐,效能優化最重要的一點便是:不要做多餘的事情。舉例:1.想要設定控制元件之間的間距,使用 margin 或 padding 之類的屬性,而不是填充一個透明的TextView2.如果你需要的效果僅是一張圖片加一串文字。那麼不需要使用兩個控制元件:TextView+ImageView. TextView一個控制元件足以。
  • 對於要被的佈局,如果沒有背景或Padding,使用 merge 標籤作為根佈局
  • 避免出現多個使用layout-weight屬性的的LinearLayout。首先我們必須要承認layout-weight的靈活性,但在使用時,請再三考慮是否真的有必要。weight將導致大量的系統開銷,每個子專案都要測量兩次。
  • 合併作為根節點的幀佈局(Framelayout)你需要知道的一個知識點:Activity或Fragment的預設根佈局是FrameLayout。如果一個幀佈局時佈局檔案中的根節點,而且它沒有背景圖片或者padding等。更有效的方式是使用標籤替換該< Framelayout />標籤 。
  • 使用組合控制元件首先說明的是,組合控制元件並不會減少過度繪製,也不會減少View的繪製時間。但它會讓你的佈局檔案看起來非常的清晰。並且對於一些條狀的控制元件。類似與下圖這樣的控制元件。當你需要給這樣的控制元件新增點選事件時,你可能需要給一個layout,兩個TextView都新增。使用組合控制元件包裝你的view,既符合封裝的特性,又可以減少程式碼量

重要的東西放到最後說:

  • 重繪控制元件,提前繪製控制元件背景與形狀,使得View在放到介面上之前就已經畫好。極為有效的避免過度繪製。說實話,直接使用原生控制元件很難避免過度繪製:一個Button,繼承與TextView,所以直接就已經被繪製了兩次(TextView一次,加Button背景第二次)那麼這是你需要終極絕招:View提前繪製。不過需要你抉擇的是,提前繪製是一項複雜的工作,所以在複雜佈局中使用OK,過於簡單的佈局就沒有必要了。且View提前繪製的適用場景是靜態View(不會平凡變化的View)。樓主正在努力將提前繪製控制元件類庫化,但目前較為遺憾的是,很難抽取提前繪製控制元件的相同點。不同需求畫法是不一樣的。

案例說明 – 登入介面

為了簡化解說,我們使用登入介面作為案例。但對於 控制元件的提前繪製來說,在登入介面投入和產出並不等比,控制元件提前繪製,你應該關注複雜的介面,尤其是這複雜的介面上還有動畫效果。

開啟過度繪製檢測後 ,上邊是我們未經優化的介面。下邊是QQ空間的登入介面。

20151103211615749

20151016211551825

 

驚訝嗎?沒關係,我們也可以做到。

之後再看我們的UI層級圖:

20151016211744054

 

下面我們一步步分析優化。

優化1

優化佈局結構,解決過深的UI層級圖,與無用的子節點

  • 過深的LinearLayout巢狀LinearLayout 巢狀LinearLayout 。並且通讀性較差

20151016211802245

優化方案1:採用相對佈局,使得佈局變淺變寬。最大可以保證只有兩層。

優化方案2:觀察可以得知,佈局整體大方向為垂直線性,採用組合控制元件(帶小圖示的輸入框作為一個整體控制元件)加垂直線性佈局。

無用的子節點:

20151016211827892

一個佈局裡只放了一個Button按鈕,次佈局為無用的子節點,去掉。

優化2

子view過久的測量時間。

20151016211844469

檢視其程式碼:

android:layout_weight=”1” 屬性導致過久的測量時間。

優化3

在Theme中給activity新增背景。減少一層繪製

優化4

重繪控制元件,提前為控制元件繪製背景或形狀,在控制元件放到佈局上時,就已經被繪製好。

這裡程式碼量較大,我們放到另一篇文章中講:

http://blog.csdn.net/u010255127/article/details/49702663

最終優化效果

沒有紅色,最高是綠色
(替換了一些控制元件和實現方式,不對本文所講述的內容有影響,我們只看過度繪製檢測)

20151103211244952

 

測試資料

【時間計算】 單位 /ms (測算時間受手機效能影響,資料較為不穩定,需多次測量)
原始狀況:

在theme加背景

僅修改佈局層次(去掉兩個不必要透明佈局)

替換自定義 提前繪製控制元件

結論

我們可以很清晰看到當我們把背景設定到Theme中時,View繪製時間減少的非常明顯。
去掉不必要的佈局和View,雖然效果甚微,但也減少了。

最後,雖然提前繪製控制元件並沒有起到減少繪製時間的作用,甚至還稍加了一點時間(記憶體中繪圖)。但減少了過度繪製,對介面執行的流暢度起到的作用非常大的。
另外,我們繪製邏輯還有很大優化的空間,這個任重而道遠……

最後提一小點,WebView裡面的Html頁面,過度繪製是檢測不到的,也就是說,如果你的Html頁面裡疊加了100層,那過度繪製檢測看起來也是一層。

我所認為的是:對Android系統來說,WebView是一個單層的View,所以不會涉及到過度繪製,但是對於WebView本身來說,View樹過深也是有效能問題的。

相關文章