閱讀本文大概需要 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內存泄露
進入公衆號,回覆“程序員“可以領取一份計算機技術電子書福利合集
歡迎轉發,關注公衆號 肖暉
每天幾分鐘,掌握一個硬核面試知識點