Android介面渲染優化
上篇介紹了android介面渲染主要是Display的過程,只要在一個時間段也就是16ms中,CPU和GPU不能正常處理完資料就會產生卡頓.
而
- CPU(中央處理器) :多快取多分支,適用於複雜的邏輯運算,主要負責Measure,Layout,Record,Execute的計算操作
- GPU(影象處理器) :眾核少快取,適用於結構單一的資料處理,主要負責 Rasterization(柵格化) 操作
實際上最後還是回到 Measure , Layout ,和 draw 上.也就是說上述三個步驟中的某一個步驟出現了耗時較長的操作,就容易導致介面卡頓。一般來說多發生在draw上。
過度繪製(Overdraw)
什麼是過度繪製?
Overdraw(過度繪製)描述的是螢幕上的某個畫素在同一幀的時間內被繪製了多次。在多層次重疊的UI結構裡面,如果不可見的UI也在做繪製的操作,會導致某些畫素區域被繪製了多次。這樣就會浪費大量的CPU以及GPU資源。

image
如何檢測過度繪製
開發者選項->除錯GPU過度繪製->顯示過度繪製區域�

image
藍色,淡綠,淡紅,深紅代表了4種不同程度的Overdraw情況,我們的目標就是儘量減少紅色Overdraw,看到更多的藍色區域
如何優化過度繪製
-
移除佈局中多餘的背景
-
layout層級扁平化,或者該用約束佈局(ConstraintLayout)
-
減少透明度的使用
移除Window預設的Background
通常,我們使用的 theme
都會包含了一個 windowBackground
,比如 theme
的如下:
<item name="android:windowBackground">@color/background_material_light</item>
然後通常我們自己的 layout
下,會另外加一層背景,這就會引起過度繪製,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" android:orientation="vertical">
一般的解決方式把 theme
下的 Background
移除
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowBackground">@null</item> </style>
或者在Activity中的onCreate()下新增
getWindow().setBackgroundDrawable(null);
去除子控制元件的背景
在開發中,經常為了一些視覺效果,需要給子控制元件附加背景.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" android:orientation="vertical"> <EditText ... android:background="@drawable/alert_edit_bg1" ... /> <LinearLayout/>
如果按照過度繪製的思想來看這個問題的話,那這裡也會村子啊過度繪製的情況,最合理的設想是如果子view有背景,並且跟父view背景不同,那麼應該移除父view的背景,保留子view的背景,這樣就能避免過度繪製了。
不過如果真的嚴格按照這個邏輯來寫程式碼的話,會導致多寫很多程式碼.況且以目前手機的效能來講,x1級別的過度繪製不太會影響到效能。
將layout層級扁平化
使用Layout Inspector去檢視layout的層次結構
老版本Android SDK提供的是一個 Hierarchy Viewer
工具,可以用來分析佈局深度,
但是在Android studio3.1之後,換成了 Layout Inspector
,入口在 Tools-->Layout Inspector
使用巢狀少的佈局
相同層級下,Fragment的效率最高,其次是LinearLayout
LinearLayout對比RelativeLayout
-
LinearLayout
在沒有設定weight屬性的時候,只會對子檢視進行一次measure
,而RelativeLayout
會在水平方向和豎直方向分別進行一次measure
- 另外,系統的
View
的measure
方法裡對繪製過程做了一個優化,如果我們或者我們的子View沒有要求強制重新整理,而父View給子View的傳入值也沒有變化(也就是說子View的位置沒變化),就不會做無謂的measure
。但是RelativeLayout
會做兩次measur
e,而在做橫向的測量時,縱向的測量結果尚未完成,只好暫時使用myHeight
傳入子View
系統,假如子View
的Height
不等於(設定了margin
)myHeight
的高度,那麼measure中上面程式碼所做得優化將不起作用,所以 儘量使用padding代替margin
ofollow,noindex">RelativeLayout和LinearLayout效能比較
使用抽象佈局 include、merge 、ViewStub
include、merge 、ViewStub這三個佈局都是官方提供的用於佈局優化的
佈局重用 include
標籤複用,比起效能上的優化,更大的作用我感覺在xml檔案的優化上
減少佈局層級 merge
<merge/>多用於替換FrameLayout或者當一個佈局包含另一個時,<merge/>標籤消除檢視層次結構中多餘的檢視組。
-
如果xml的根部局是
FrameLayout
,直接替換成merge
就能減少一個佈局深度。 -
如果你的主佈局檔案是垂直佈局,引入了一個垂直佈局的
include
,這個時候就可以將include
的根佈局替換成merge
延遲載入 ViewStub
ViewStub
是一個不可見的,大小為0的 View
,能為xml載入減小不少壓力。常用於 進度條、顯示錯誤訊息 等不需要第一時間顯示的檢視, inflate()
之後會被對應的layout所代替
注:ViewStub目前有個缺陷就是還不支援 <merge /> 標籤。
使用lint來優化佈局的層次結構
Analyze->Inspect code
佈局效能方面的資訊位於 Android
> Lint
> Performance
下,我們可以點開它來看下一些優化建議。
下面是lint的一些優化技巧:
- 使用複合圖片
如果一個線性佈局中包含一個ImageView
和一個TextView
,可以使用複合圖片來替換掉 - 合併根節點
如果一個FrameLayout
是整個佈局的根節點,並且也沒有提供背景、留白等等,那麼可以使用<merge>
標籤來替換掉,因為DecorView
本身就是一個FrameLayout
。 - 移除佈局中無用的葉子
佈局是一個樹形的結構,如果一個佈局沒有子View
或者背景,那麼可以把它移除掉(這佈局本身就不可見了)。 - 移除無用的父佈局
如果一個佈局沒有兄弟,也不是ScrollView
或者根View
,並且也沒有背景,那麼可以把這個父佈局移除掉,然後把它的子view
移到它的父佈局下。 - 避免過深的層次結構
過多的佈局巢狀不利於效能,可以使用更扁平化的佈局,如RelativeLayout
、GridLayout
、ConstraintLayout
等佈局來提高效能。佈局預設的最大深度為10
減少透明度的使用
對於不透明的 view
,只需要渲染一次即可把它顯示出來。但是如果這個 view
設定了 alpha
值,則至少需要渲染兩次。這是因為使用了 alpha
的 view
需要先知道混合 view
的下一層元素是什麼,然後再結合上層的 view
進行Blend混色處理。透明動畫、淡入淡出和陰影等效果都涉及到某種透明度,這就會造成了過度繪製。可以通過減少渲染這些透明物件來改善過度繪製。比如:在 TextView
上設定帶透明度 alpha
值的黑色文字可以實現灰色的效果。但是,直接通過設定灰色的話能夠獲得更好的效能。
減少自定義View的過度繪製,使用clipRect()和QuickReject()
-
clipRect():可以指定一塊矩形區域,只有在這個區域內才會被繪製,其他的區域會被忽視,可以有效的節約CPU與GPU的資源
canvas.clipRect(0, 0,200,200);//指定(0,0)到(200,200)的矩形區域為繪製區域
-
QuickReject():判斷畫布是否與你指定的矩形相交,藉此來決定是否需要繪製該畫布
boolean skipDraw=canvas.quickReject(new RectF(), Canvas.EdgeType.AA);
onDraw()中不要建立新的區域性變數以及不要做耗時操作
onDraw()
中不要建立新的區域性變數,因為 onDraw()
方法可能會被頻繁呼叫,大量的臨時物件會導致記憶體抖動,會造成頻繁的GC,從而使UI執行緒被頻繁阻塞,導致畫面卡頓。這種問題一般android studio的 lint靜態檢查 都會報警告,平時多注意些android studio標黃的警告,有助於效能調優
嘗試使用ConstraintLayout
-
較高的效能優勢
比起LinearLayout 、RelativeLayout等,ConstraintLayout可以用更少的佈局層級來達到相同的效果
-
完美的螢幕適配
ConstraintLayout的大小、距離都可以使用比例來設定,所以其適配性更好。
當然不是任何時候都推薦使用 ConstraintLayout
的,在簡單的佈局下還是建議使用 LinearLayout
、 RelativeLayout
,因為對於相同層級的佈局下, ConstraintLayout
的 measure,layout,draw 等過程會顯得更加重.
Android 約束佈局 ConstraintLayout圖形化操作
參看文件
那些 Android 程式員必會的檢視優化策略