實戰 Android中的UI過度繪制

相信很多人都有這種經歷,在使用app的過程中,突然間發現程序雖然在運行,但是這里停頓一下,那里停頓一下的卡頓現象,就像看上網看視頻一樣,緩沖不過來,視頻很卡,不能連續的看下去。造成這樣原因有很多,其中一種就是UI被過度繪制了。
UI過度繪制簡單的來說是指在一個界面中有很多元素,但是我們只需要更新某一小塊的元素,app卻把所有的元素都刷新一遍,這就造成過度繪制。

overdraw_hidden_view.png

過度繪制造成UI卡頓的原因是因為它浪費大量的CPU以及GPU資源。手機原本為了保持視覺的流暢度,其屏幕刷新頻率是60hz,即在1000/60=16.67ms內更新一幀。如果沒有完成任務,就會發生掉幀的現象,也就是我們所說的卡頓。
這其中的原理比較復雜,大家可以看看大神是怎么說的,這里給個胡凱大神的文章地址:

Android性能優化之渲染篇
http://hukai.me/android-performance-render/

debug GPU overdraw

有問題就必然有解決辦法,在Android系統內部也有一個神器可以查看app的UI的過度繪制情況,在開發者選項中有個debug GPU overdraw(調試GPU過度繪制),打開之后有off(關閉),show overdraw areas(顯示過度繪制區域),show areas for Deuteranomaly(為紅綠癥患者顯示過度繪制區域)


overdraw.png

我們選擇show overdraw areas,發現整個手機界面的顏色變了,在打開過度繪制選項后,其中的藍色,淡綠,淡紅,深紅代表了4種不同程度的Overdraw情況,我們的目標就是盡量減少紅色Overdraw,看到更多的藍色區域。


color.png

Profile GPU rendering

其次android系統還內置了Profile GPU rendering工具,這個工具也是在開發者選項中打開,它能夠以柱狀圖的方式顯示當前界面的渲染時間


Profile GPU rendering.png
  • 藍色代表測量繪制的時間,或者說它代表需要多長時間去創建和更新你的DisplayList.在Android中,一個視圖在可以實際的進行渲染之前,它必須被轉換成GPU所熟悉的格式,簡單來說就是幾條繪圖命令,復雜點的可能是你的自定義的View嵌入了自定義的Path. 一旦完成,結果會作為一個DisplayList對象被系統送入緩存,藍色就是記錄了需要花費多長時間在屏幕上更新視圖(說白了就是執行每一個View的onDraw方法,創建或者更新每一個View的Display List對象).
  • 橙色部分表示的是處理時間,或者說是CPU告訴GPU渲染一幀的地方,這是一個阻塞調用,因為CPU會一直等待GPU發出接到命令的回復,如果柱狀圖很高,那就意味著你給GPU太多的工作,太多的負責視圖需要OpenGL命令去繪制和處理.
  • 紅色代表執行的時間,這部分是Android進行2D渲染 Display List的時間,為了繪制到屏幕上,Android需要使用OpenGl ES的API接口來繪制Display List.這些API有效地將數據發送到GPU,最總在屏幕上顯示出來.

在這里也放一個大神關于Profile GPU rendering的介紹

http://androidperformance.com/2015/04/19/Android-Performance-Patterns-4.html

下面我們開始對UI多度繪制開始實戰吧!
實戰項目地址

安卓UI問題
https://github.com/lzyzsd/AndroidUIPorblems

初始界面的問題

剛打開這個項目,我們就發現了在第一個有過度繪制問題,效果如下


main界面過度繪制.PNG

存在問題

  • 在按鈕overdraw上面就有個紅色的過度繪制區域
  • 在文本框This is test的布局中也是紅色過度繪制區域

解決方法

  • 要解決這個問題,我們首先需要分析這是怎么引起的。分析到activity_main.xml的布局文件時,發現這里使用了多個嵌套的LinearLayout布局,而且每個LinearLayout都會使用一次android:background設置一次自己的背景顏色,他們造成了過度繪制。
    仔細分析在其中一個嵌套ImageView的LinearLayout布局背景顏色與最外層的背景顏色是一樣的,屬于不需要的背景色,因此將這個LinearLayout中的android:background屬性刪除,這時發現文本框布局已經不再是紅色了


    第一次優化.png
  • 咋看之下一切都很完美,但其實整個ui其實還含有一個隱含的繪制效果,那邊是在activity中,使用setContentView(R.layout.activity_main)設置布局的時候,android會自動填充一個默認的背景,而在這個UI中,我們使用了填充整個app的背景,因此不需要默認背景,取消也很簡單,只需要在activity中的onCreate方法中添加這么一句就行了

getWindow().setBackgroundDrawable(null);

現在看最終優化效果


最后結果.png

OVERDRAWVIEW頁面的問題

在overdrawviewactivity中只有一個自定義的圖案,而這個自定義的圖案引起了過度繪制的問題


OVERDRAWVIEW過度繪制.PNG

解決方法

  • 首先這個也是填充了整個ui界面的繪制圖片,因此我們也在activity中的onCreate方法中添加getWindow().setBackgroundDrawable(null);取消默認繪制。
  • 繼續研究,發現過度繪制問題是由于OverDrawView類中的ondraw方法中多次繪制了矩形導致的,代碼如下:

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
mPaint.setColor(Color.GRAY);
canvas.drawRect(0, 0, width, height, mPaint);
mPaint.setColor(Color.CYAN);
canvas.drawRect(0, height/4, width, height, mPaint);
mPaint.setColor(Color.DKGRAY);
canvas.drawRect(0, height/3, width, height, mPaint);
mPaint.setColor(Color.LTGRAY);
canvas.drawRect(0, height/2, width, height, mPaint);
}

通過分析得知,顏色為GRAY的矩形的高度其實不需要設置為整個屏幕的高度,它的高度只需要設置為它所顯示范圍的高度就可以了,因此可以設為height/4。
其他的矩形也是同樣的道理,因此更改這里的代碼為:

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

     int width = getWidth();
      int height = getHeight();
    mPaint.setColor(Color.GRAY);
      canvas.drawRect(0, 0, width, height/4, mPaint);
    mPaint.setColor(Color.CYAN);
      canvas.drawRect(0, height/4, width, height/3, mPaint);
     mPaint.setColor(Color.DKGRAY);
      canvas.drawRect(0, height/3, width, height/2, mPaint);
      mPaint.setColor(Color.LTGRAY);
    canvas.drawRect(0, height/2, width, height, mPaint);
}

優化的界面


最終優化.png

BUSYONDRAW頻繁繪制

當我們點擊BUSYONDRAW按鈕的時候,我們發現明顯的卡頓現象。在開發者選項中打開Profile GPU rendering選項,然后在次點擊BUSYONDRAW按鈕,發現這個頁面繪制渲染時間已經突破天際了!


繪制時間1.png

在初始的時候,藍色繪制時間占滿整個屏幕高度,這是造成卡頓的重要原因。

解決方法

  • 首先卡頓現象是由ondraw方法中的for循環中的打印字符串引起的

for (int i = 0; i < 1000; i++) {
System.out.println("canvas = [" + canvas + "]" + i);
}

我們將其提取出來放在另一個線程中運行。
先是創建一個線程池:

private ExecutorService pool = Executors.newCachedThreadPool();

將要運行的耗時操作封裝成Runable對象并通過一個方法獲取:

@NonNull
private Runnable getCommand(final Canvas canvas) {
return new Runnable() {
@Override
public void run() {
for (int i = 0; i &amp;lt; 1000; i++) {
System.out.println("canvas = [" + canvas + "]" + i);
}
}
};
}

最后在onDraw里運行放在子線程里運行:

pool.execute(getCommand(canvas));
現在進入也不會卡頓了。

  • 其次在ondraw中也不宜創建Paint()對象,因為app會頻繁調用ondraw對象,會造成內存泄漏,因此需要將其提取為全局變量。

  • 最后繪制了多個圓形圖案也造成了一定程度的卡頓,但由于功力不夠,暫時無法優化。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容