安卓過度繪製

1.概述

  UI渲染操作通常依賴於兩個核心組件:CPU與GPU。CPU負責包括Measure,Layout,Record,Execute的計算操作,GPU負責Rasterization(柵格化)操作。   所謂柵格化,就是將那些UI控件(如Button,Bitmap)拆分到不同的像素上進行顯示。這是一個很費時的操作,GPU能夠加快柵格化的操作。   爲了能夠使得App流暢,在Android中我們有一個16ms準則。這是因爲Android平臺的屏幕刷新頻率一般爲60Hz,也就是大概16.6ms一幀。也就是說在16ms內我們的CPU和GPU必須完成所有的計算,繪製,渲染操作,否則就有可能出現掉幀,卡頓現象,引發性能問題,傷害用戶體驗。   過度繪製(Overdraw)指的是屏幕上的某個像素在同一幀(16ms)的時間內被繪製了過多次數。   有很多原因可以導致丟幀,也許是因爲你的layout太過複雜,無法在16ms內完成測量和佈局,有可能是因爲你的UI上有層疊太多的繪製單元,還有可能是因爲動畫執行的次數過多。也有可能是是主線程中做了其他耗時操作,或者線程數量太多。這些都會導致CPU或者GPU負載過重。

2.GPU過渡繪製調試

開啓:adb shell setprop debug.hwui.overdraw show

關閉:adb shell setprop debug.hwui.overdraw false

3.過度繪製的分級:

原色:沒有過度繪製

藍色:1 次過度繪製

綠色:2 次過度繪製

粉色:3 次過度繪製(不應超過屏幕的四分之一)

紅色:4 次及以上過度繪製(不能出現)

4.避免過度繪製

4.1去掉Activity的冗餘的背景

(1)當我們新建一個Activity時,Activity的Theme主題上往往自動有一個背景。但是很多時候這個背景是多餘的,我們自己使用的控件往往已經設置了背景;

在APP Theme中把背景改掉:

<style name="AppTheme" parent="android:Theme.Light.NoTitleBar">
    <item name="android:windowBackground">@null</item>
    ...
</style>

2)還有很多時候我們在Activity的佈局文件中的根Layout上就已經設置了background屬性,這樣的情況下,這個Activity自帶的背景就多餘了。

在Activity的onCreate()中試用以下代碼:

          getWindow().setBackgroundDrawable(null);


如果是在Fragment中,就是

           getActivity().getWindow().setBackgroundDrawable(null)

4.2 ImageView的background和setImageDrawable()重疊

  我們平時使用ImageView等類似的顯示圖片的控件時,通常會有一個加載未完成時的佔位圖,很多時候我們習慣用background屬性進行添加。等到網絡將圖片請求到之後我們又使用setImageDrawable()設置了圖片,這樣子圖片會覆蓋掉原來的佔位圖,但是Android系統繪製界面時原來的佔位圖和現在的圖片都會被繪製,造成了Overdraw。

就是佔位圖和後來加載到的圖片都使用setImageDrawable()來設置,而不要使用background

4.3 減少Drawable的複雜Shape

  使用複雜的Shape有時候也是引起過度繪製的一個因素,比如Stroke,雖然只是描邊,但是會增加整個區域的一層過度繪製。例如,將下面的配置作爲ImageView的背景。

在有stroke屬性的時候,顯示爲淡紅色,去掉stroke屬性後,顯示爲綠色。

4.4使視圖層級扁平化

  現代佈局使視圖堆疊和分層更加容易。然而,這樣做卻會過度繪製,從而導致性能降低。特別在每一個堆疊視圖對象是不透明的場景中,可見和不可見的像素都需要繪製在屏幕上。

  如果遇到這類問題,可以通過優化視圖層次結構來減少重疊的UI對象數量,從而提升性能。

要想扁平化佈局效果,可以使用約束性佈局ConstraintLayout。

4.5自定義控件中onDraw函數的正確使用

  在自定義控件中,我們經常會通過onDraw函數繪製一些複雜的形狀、文字、圖片等。但是這些Canvas的繪製函數,每一次調用就會增加一層的繪製,次數越多也就會導致越多的過度繪製。如我們用如下代碼來自定義繪製圖形,就會存在繪製三次的情況:

4.6通過Merge標籤減少View樹層次

  我們在自定義可重用佈局的時候,都會新建一個文件來存放這個佈局。當我們在文件中include這個佈局文件的時候,很可能就額外多了一層的佈局。如果這時候使用Merge的話,再使用包含這個的佈局的時候,系統會自動忽略merge層級,把它的子view直接放置與include平級的佈局下。還有一種情況,例如在LinearLayout等佈局裏面嵌入一個佈局時,剛好這個佈局的根節點也是LinearLayout等佈局的時候,既兩層佈局使用了同一種類型的佈局時,這樣就多了一層沒有用的嵌套,而這個時候如果使用merge根標籤就可以避免這樣的問題,減少這層無用的嵌套。

  不過只能作爲XML佈局的根標籤使用。當Inflate以開頭的佈局文件時,必須指定一個父ViewGroup,並且必須設定attachToRoot爲true。

4.7通過ViewStub標籤優化佈局

  一些顯示錯誤的界面、加載提示框的界面,ProgressBar等界面,以及用戶很少會觸發的界面,當這些不是必須顯示的佈局都堆在一起時,會對infalte性能、內存等產生不利影響。這個時候用ViewStub標籤就很合適,可以將這些不常用的佈局另外保存爲佈局文件,對應的位置佈局用ViewStub標籤代替。等到真正需要顯示的時候再對ViewStub初始化。因爲ViewStub是一個輕量級的View,它是一個不可見,且不佔佈局位置,佔用資源非常小的控件。所以使用ViewStub後,可以加快Inflate的時間,減少創建的對象數量,從而提升性能和減少內存的佔用。不過ViewStub只能Inflate一次,一旦初始化後該標籤就會被真正的佈局所代替。

4.8減少不需要的View

  一方面減少層級本身就是減少了view的數量,另外像一些場景,文字有大有小,有不同顏色,其實用一個TextView就可以實現,例如可以藉助Spannable對象實現。不需要使用很多個TextView來實現。

另外,有時我們發現一個空白區域的佔位也使用了一個view去做,其實用layout_margin屬性或者用其他方式就能代替掉這個View。

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