android性能優化(一)之UI渲染優化

閱讀本文大概需要 2.6 分鐘。

 

 

在衆多高頻面試題中,Android性能優化幾乎可以說是必問的考題。

 

而此題一出,一場惡戰已然拉開序幕,因爲此話題牽扯麪非常廣,絕非三言兩語就能夠聊完。因此,非常有必要對性能優化做一下系統性的總結。

 

此篇作爲性能優化系列開篇,是因爲UI渲染優化最爲初級,且雜項很多,面試時可以作爲回答的起點。

 

對於UI渲染優化,記住一個宗旨:儘量減少layout佈局嵌套層級和View個數。後面幾項其實都是圍繞着這個宗旨來做具體的優化的。

 

減少嵌套層級是減少View樹的深度,原因一是如果View樹過深,會導致findViewById()方法查找耗時,因爲findViewById始終是從rootView開始深度優先遍歷,二是會導致繪製重疊,從而造成過度繪製。

 

減少View個數,不言而喻能用最少的View個數實現UI效果,能夠避免不必要的測量,放置,繪製的計算時間,和View的查找時間。

 

從代碼的可讀性和可維護性上來講,最簡潔的東西也是最好的。

 

一. 佈局優化

 

1)儘量選用RelativeLayout,ConstrantLayout,LinearLayout,併發揮它們的特性使佈局扁平化。同等扁平的情況下,選用LinearLayout性能更佳。

 

2)利用系統提供View的特殊屬性,比如TextView的drawableStart屬性等設置icon,就沒有必要再增加一個ImageView。TextView滑動屬性來避免嵌套一層ScrollView。

 

3)include一個layout時,可以考慮使用merge標籤,如果佈局的最外層和它所在的父容器控件相同,那麼使用merge可以減少一個嵌套層級。

 

4)當某個佈局的顯現需要條件時,比如斷網,獲取數據失敗等顯示的UI,可以考慮使用ViewStub。其本質是一個寬高爲0的View,非常輕量級,當需要顯示時再inflate或者visible=true纔會加載其佈局。

 

5)使用LinearLayout自帶的divider屬性實現分割線,而不是在佈局中手動添加一個額外的View作爲分割線。

 

6)使用Space控件(而非View,LinearLayout等)進行合理的佔位。其本質是一個onDraw實現爲空的View,因此它只佔位置,而不去渲染,使用它來進行佔位填充比其它控件更加高效。

 

二. 自定義view優化

 

1)onDraw中避免對象的分配。原因是onDraw會執行多次,將導致頻繁的GC,內存抖動,損耗性能。

2)使用Canvas的ClipRect方法避免過度繪製。

 

三.過度繪製

 

所謂過度繪製,就是GPU在同一區域進行了不必要的多次繪製。如果進行真機調試,打開“調試GPU過度繪製”,就能看到不同的區域有不同的顏色,表示過度繪製嚴重的程度。

 

過度繪製的原因就是嵌套層級過深,而且設置了多餘的背景色和不可見區域的繪製所導致。

 

1)在合適的地方設置背景。

 

比如要對Activity設置背景顏色,無需在其根Layout上設置其background,因爲系統本身就是把Activity的xml佈局添加到一個叫“content”的FrameLayout中,Android系統本身已經對這個FrameLayout設置了背景顏色。

 

如果我們在layout.xml的根View中再設置背景顏色,就相當於GPU在同一區域繪製了2次背景色,導致過度繪製。

 

解決的辦法有2種:

public class MyActivity extends AppCompatActivity {
    @Override  
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       //在setContentView之前
       findViewById(android.R.id.content).setBackgroundResource(xxx);
       setContentView(R.layout.xxx); //在自己的根layout中設置背景
}

 

public class MyActivity extends AppCompatActivity {
    @Override  
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       
       setContentView(R.layout.xxx); //在自己的根layout中設置背景
       getWindow().setBackgroundDrawable(null) //在setContentView之後
}

 

2)不可見區域不要繪製,ClipRect & QuickReject。

當我們在自定義View中用到ClipRect時,請只在ClipRect這個可見的矩形區域內進行繪製,而且通過QuickReject來判斷另一個繪製的矩形區域是否和ClipRect所在矩形區域有相交,若無,則可避免區域外的繪製。

 

 

一切從android的handler說起(一)之message

一切從android的handler說起(二)之threadLocal

一切從android的handler說起(三)之UI線程不卡頓

一切從android的handler說起(四)之postDelay原理

一切從android的handler說起(五)之觸摸事件模型

一切從android的handler說起(六)之生命週期來源

一切從android的handler說起(七)之Handler內存泄露


 

進入公衆號,回覆“程序員“可以領取一份計算機技術電子書福利合集

歡迎轉發,關注公衆號 肖暉

每天幾分鐘,掌握一個硬核面試知識點

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章