Android UI性能優化 – Overdraw

什麼是Overdraw?

Overdraw就是屏幕上某個像素點在同一幀被繪製了多次。在多層佈局結構中,不可見部分也會被繪製。舉個例子,一個白色頁面上有一個按鈕。系統首先繪製白色背景,然後在白色背景上繪製按鈕,最後在按鈕背景上繪製按鈕內容。那麼按鈕和按鈕內容就造成了Overdraw。 
其實Overdraw是不可避免的,我們要做的是檢查Overdraw次數過多的地方,並刪掉無用的繪製。

如何檢查Overdraw?

在開發者工具中勾選Show GPU overdraw選項,觀察UI上的Overdraw情況。


screenshot


該工具會以不同的顏色繪製在屏幕上,標識出該塊UI的Overdraw情況。


screenshot


如果是透明色,表示只繪製了一次,沒有Overdraw。 
我們要做的就是減少大面積的紅色區域,使其變成藍色甚至是透明色。對於深紅色,就要思考出了什麼問題,堅決進行優化。

優化菜鳥裹裹物流詳情頁面

首先打開Show GPU overdraw選項,觀察一下情況。


screenshot


相當恐怖,幾乎全是深紅色。

分析Overdraw原因

佈局結構:基於ListView展示,快遞信息、物品信息、小件員信息在同一個View封裝,作爲HeaderView加入ListView,ListView的item展示物流跟蹤信息。


screenshot

繪製層級:

第一層:window背景 
第二層:Activity中fragment Container背景 
第三層:Fragment中ListView背景 
第四層:ListView的Header背景和物流信息item背景 
第五層:快遞信息、物品信息、小件員信息背景和物流信息item內容 
第六層+:快遞信息、物品信息、小件員信息內容

開始優化

第一步,刪除window默認背景

Android系統提供了默認的背景,被DecorView持有,但是通常情況,我們會爲全屏提供自定義顏色,那麼這個背景就沒用了,反而會帶來一次Overdraw的性能損耗。 
Activity的onCreate方法中,調用getWindow().setBackgroundDrawable(null)即可刪除這個默認背景。 
注意,這個方法應該在setContentView後調用。原因通過分析源碼可知: 
setContentView通過調用PhoneWindow的setContentView,再到installDecor。最終通過以下代碼設置背景:

if (mDecor.getBackground() == null &&mBackgroundFallbackResource != 0) {
    mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

而getWindow().setBackgroundDrawable(null)同樣會調用PhoneWindow的以下方法:

public final void setBackgroundDrawable(Drawable drawable) {
    if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {
        mBackgroundResource = 0;
        mBackgroundDrawable = drawable;
        if (mDecor != null) {
            mDecor.setWindowBackground(drawable);
        }
        if (mBackgroundFallbackResource != 0) {
            mDecor.setBackgroundFallback(drawable != null ? 0 : mBackgroundFallbackResource);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以看出,如果先調用getWindow().setBackgroundDrawable(null), setContentView會再次設置背景色。 
因此,getWindow().setBackgroundDrawable(null)應該在setContentView之後調用。

第二步,刪除佈局中重複設置的背景

整個佈局基於ListView全屏展示,因此和Activity中fragment container背景衝突,保留其中之一即可,在這裏選擇刪除ListView的背景,由container設置整體背景。


screenshot


優化效果初見成效,滿屏的紅色沒有了,取而代之的是大塊的綠色和藍色,小塊淡紅色和極少的深紅色。

第三步,優化HeaderView

從UI設計來看,HeaderView的幾個區塊是白色背景,中間用灰色分割。 
原來的實現方案是root view設置灰色背景,sub view使用margin暴露root view底色。如下圖:


screenshot


這裏是存在優化空間的,可以將雙層結構改爲單層,區塊之間的分割使用灰色背景的view即可。修改後的結構如下圖:


screenshot


通過佈局結構的優化,可以使HeaderView再減少一次Overdraw。

第四步,細粒度優化

觀察第二步優化後的情況,可以發現,物品信息TextView和快遞圖標ImageView還是存在深紅色。 
檢查佈局文件發現,TextView設置了和parent view相同的背景色,這很簡單,刪除即可。 
快遞圖標使用了兩個ImageView組成,一個展示快遞圖標,另一個爲圖標增加了邊框。我將兩個ImageView進行合併,通過src屬性展示圖標,background屬性設置邊框,並增加了1px的padding把邊框展示出來。 
而物品圖標使用了自定義的ImageView,使用上述優化手段存在bug,暫不做處理。 
至此,優化工作完成,下圖是優化後的效果。


screenshot


可以看出,整屏基本上都是藍色,少量綠色以及小面積的淡紅色,基本達到了優化效果。

最後,總結一下優化步驟

  1. 刪除window默認背景。
  2. 刪除多層佈局結構中重複設置的背景
  3. 優化佈局,減少佈局層次。
  4. 檢查組件,刪繁去冗。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章