Android之GPU過度繪制與圖形渲染優化
寫在前面的話
本文主要對過度繪制和圖形渲染做一個概念性的描述,和簡單的優化措施。
如果你已對過度繪制有過一些了解,那么你應該明白,僅是簡單的層級優化對過度繪制的改善是很小的。所以,這時候你可以參考這篇文章:
另外如果你還想知道更多關於View優化原理,可以參考這篇文章:
http://www.oschina.net/news/60157/android-performance-patterns
1. 概念
GPU過度繪制:
是指在一個像素點上繪制多次(超過一次)。舉一個簡單的例子:顯示一個什么都沒有做的activity界面算作畫了1層,給activity加一個背景是第2層,在上面放了一個Text View(有背景的Text View)是第3層,Text View顯示文本就是第4層 。
僅僅只是為了顯示一個文本,卻在同一個像素點繪制了四次,這是一定要優化的!
還有,過度繪制對動畫性能的影響是極其嚴重的。如果你想要流暢的動畫效果,那么一定不能忽視過度繪制!!
圖形渲染優化:
一個View的繪制過程:測量、布局、畫圖。三者的累積時間,就是一個View的最終繪制時間 。 過多的層級、無用的子節點父節點、過於依賴系統計算位置的布局屬性(如: weight)。都會引起上述三個過程時間的增加。
2.關鍵點/字
-
過渡繪制優化與圖形渲染優化都其目的都是為了提供一個高效的UI。其目的相似,優化方式也有相同之處,所以一起進行總結。
-
調試GPU過渡繪制顏色區域說明
無/白色:繪制1次 藍色:繪制2次(理想狀態) 綠色:繪制3次 淺紅:繪制4次(要優化了) 深紅:繪制5次或5次以上。(必須要優化了)
-
調試Hierarchy Viewer 顏色說明
下方三個原點從左到右:測量、布局、畫圖時間 紅色:該View所用時間超過大部分View很多 黃色:該View所用時間超過大部分View 綠色:該View所用時間低於大部分View
-
引起過度繪制的兩個主要因素:層級與背景圖片
- 層級為透明時(不添加背景),不會引起過度繪制,但會引起測量、布局、畫圖時間的顯著提高。 - 改變View形狀,也算是繪制一層。添加一個橢圓形的黑色背景,算作兩層 - 值得注意的是,背景圖片的繪制是及其耗時的
-
一個通常的錯誤觀念就是使用基本的布局結構(例如:LinearLayout、FrameLayout等)能夠在大多數情況下產生高效率的布局。
基本的線性布局會導致過於累贅的層級嵌套結構。使用相對布局優化。 但並不是所有情況下都應該用相對布局。(相對布局過於復雜,且通讀性差)應考慮權衡關系。
3.優化措施
-
在Theme中給activity增加背景。使用WindowBackground屬性。
背景的繪制是非常耗時的,在Theme中添加背景,不算繪制一層,並且View渲染時間減少很多。 具體原因詳解請參考:
-
減少層級,沒必要的背景圖
(如果一個View和它所在的Layout的顏色相同,就不需要給兩個都設置背景)
-
避免使布局太深,而應該讓布局更淺更深
(用相對布局替換線性布局)
-
無用的子節點、父節點刪除
沒有免費的午餐,性能優化最重要的一點便是:不要做多余的事情。舉例: 1.想要設置控件之間的間距,使用 layout_marginTop 之類的屬性, 而不是填充一個透明的TextView 2.如果你需要的效果僅是一張圖片加一串文字。那么不需要使用兩個控件:TextView+ImageView. TextView一個控件足以。
-
對於要被的布局,如果沒有背景或Padding,使用 merge 標簽作為根布局
-
避免出現多個使用layout-weight屬性的的LinearLayout。
首先我們必須要承認layout-weight的靈活性,但在使用時,請再三考慮是否真的有必要。 weight將導致大量的系統開銷,每個子項目都要測量兩次。
-
合並作為根節點的幀布局(Framelayout)
你需要知道的一個知識點:Activity或Fragment的默認根布局是FrameLayout。 如果一個幀布局時布局文件中的根節點,而且它沒有背景圖片或者padding等。 更有效的方式是使用<merge />標簽替換該< Framelayout />標簽 。
-
使用組合控件
首先說明的是,組合控件並不會減少過度繪制,也不會減少View的繪制時間。 但它會讓你的布局文件看起來非常的清晰。 並且對於一些條狀的控件。類似與下圖這樣的控件。 當你需要給這樣的控件添加點擊事件時,你可能需要給一個layout,兩個TextView都添加。 使用組合控件包裝你的view,既符合封裝的特性,又可以減少代碼量
重要的東西放到最后說:
-
重繪控件,提前繪制控件背景與形狀,使得View在放到界面上之前就已經畫好。極為有效的避免過度繪制。
說實話,直接使用原生控件很難避免過度繪制: 一個Button,繼承與TextView,所以直接就已經被繪制了兩次(TextView一次,加Button背景第二次) 那么這是你需要終極絕招:View提前繪制。 不過需要你抉擇的是,提前繪制是一項復雜的工作,所以在復雜布局中使用OK,過於簡單的布局就沒有必要了。 樓主正在努力將提前繪制控件類庫化,但目前較為遺憾的是,很難抽取提前繪制控件的相同點。 不同需求畫法是不一樣的。
4. 案例說明 – 登錄界面
為了簡化解說,我們使用登錄界面作為案例。但對於 控件的提前繪制來說,在登錄界面投入和產出並不等比,控件提前繪制,你應該關注復雜的界面,尤其是這復雜的界面上還有動畫效果。
開啟過度繪制檢測后 ,上邊是我們未經優化的界面。下邊是QQ空間的登錄界面。
驚訝嗎?沒關系,我們也可以做到。
之后再看我們的UI層級圖:
下面我們一步步分析優化。
優化1:
優化布局結構,解決過深的UI層級圖,與無用的子節點
- 過深的LinearLayout嵌套LinearLayout 嵌套LinearLayout 。並且通讀性較差
優化方案1:采用相對布局,使得布局變淺變寬。最大可以保證只有兩層。
優化方案2:觀察可以得知,布局整體大方向為垂直線性,采用組合控件(帶小圖標的輸入框作為一個整體控件)加垂直線性布局。
- 無用的子節點。
一個布局里只放了一個Button按鈕,次布局為無用的子節點,去掉。
優化2
子view過久的測量時間。
查看其代碼:
<com.envision.mobile.ui.widget.EditView
android:id="@+id/login_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="8dip"
android:layout_weight="1"
android:background="@null"
android:hint="@string/hint_login_name"
android:imeOptions="actionNext"
android:paddingBottom="8dip"
android:singleLine="true"
android:textColor="@color/white"
android:textColorHint="@color/white_trans_88"
android:textCursorDrawable="@null" />
android:layout_weight=”1” 屬性導致過久的測量時間。
優化3
在Theme中給activity添加背景。減少一層繪制
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:windowBackground">
@drawable/bg_homepage
</item>
</style>
優化4:
重繪控件,提前為控件繪制背景或形狀,在控件放到布局上時,就已經被繪制好。
這里代碼量較大,我們放到另一篇文章中講:
http://blog.csdn.net/u010255127/article/details/49702663
最終優化效果
沒有紅色,最高是綠色
(替換了一些控件和實現方式,不對本文所講述的內容有影響,我們只看過度繪制檢測)
測試數據
【時間計算】 單位 /ms (測算時間受手機性能影響,數據較為不穩定,需多次測量)
原始狀況:
onCreate 到 onStart : 381 370
onCreate 到 onResume :383 373
onStart 到 onResume : 2 3
在theme加背景
onCreate 到 onStart : 274 295
onCreate 到 onResume :277 298
onStart 到 onResume : 3 3
僅修改布局層次(去掉兩個不必要透明布局)
onCreate 到 onStart : 272 276
onCreate 到 onResume :274 279
onStart 到 onResume : 2 3
替換自定義 提前繪制控件
onCreate 到 onStart : 289 299 294
onCreate 到 onResume : 292 302 296
onStart 到 onResume : 3 3 2
結論:我們可以很清晰看到當我們把背景設置到Theme中時,View繪制時間減少的非常明顯。
去掉不必要的布局和View,雖然效果甚微,但也減少了。
最后,雖然提前繪制控件並沒有起到減少繪制時間的作用,甚至還稍加了一點時間(內存中繪圖)。但減少了過度繪制,對界面運行的流暢度起到的作用非常大的。
另外,我們繪制邏輯還有很大優化的空間,這個任重而道遠……
最后提一小點,WebView里面的Html頁面,過度繪制是檢測不到的,也就是說,如果你的Html頁面里疊加了100層,那過度繪制檢測看起來也是一層。
我所懷疑的是,這一層究竟是表象,只是避開了工具的檢測。還是真的可以起到性能優化作用。對WebView研究甚少,還請大神們研究一下。