Android性能優化(二)佈局渲染流程及優化

一、佈局渲染流程

我們平常開發中的那些控件,比如Button,TextView,是如何渲染到屏幕上的呢?
這裏寫圖片描述

簡而言之,就是現先將xml解析成相應的對象,然後CPU經過計算以後將圖形信息傳給GPU,GPU來負責繪製,柵格化等操作,最終顯示到手機屏幕上。

二、爲什麼會出現卡頓

這裏寫圖片描述

Android每16ms對屏幕進行一次刷新,當一幀畫面渲染時間超過16ms的時候,垂直同步機制會讓顯示器硬件等待GPU完成柵格化渲染操作,最後在下一次VSync信號到來時才顯示到屏幕上,這樣就會讓這一幀畫面,在屏幕上多停留了16ms

這裏寫圖片描述

如上圖
Step1. Display顯示第0幀數據,此時CPU和GPU渲染第1幀畫面,而且趕在Display顯示下一幀前完成
Step2. 因爲渲染及時,Display在第0幀顯示完成後,也就是第1個VSync後,正常顯示第1幀
Step3. 由於某些原因,比如CPU資源被佔用,系統沒有及時地開始處理第2幀,直到第2個VSync快來前纔開始處理
Step4. 第2個VSync來時,由於第2幀數據還沒有準備就緒,顯示的還是第1幀。這種情況被Android開發組命名爲「Jank」。
Step5. 當第2幀數據準備完成後,它並不會馬上被顯示,而是要等待下一個VSync。
所以總的來說,就是屏幕平白無故地多顯示了一次第1幀。原因大家應該都看到了,就是CPU沒有及時地開始着手處理第2幀的渲染工作,以致「延誤軍機」。

試想用戶盯着同一張圖看了32ms而不是16ms,當然很容易察覺出卡頓感,哪怕僅僅出現一次掉幀,用戶都會發現動畫不是很順暢。

三、如何解決這種問題

16 毫秒的時間主要被兩件事情所佔用
第一件:將 UI 對象轉換爲一系列多邊形和紋理
第二件: CPU 傳遞處理數據到 GPU 。所以很明顯,我們要縮短
這兩部分的時間,也就是說需要儘量減少對象轉換的次數,以及上傳數據的次數

如何減少這兩部分的時間 以至於在 16ms 完成呢?

(1)CPU 減少 xml 轉換成對象的時間(去掉重複的、減少不必要的佈局,儘量扁平化)
(2)GPU 減少重複繪製的時間

四、什麼是過度繪製

GPU的繪製過程,就跟刷牆一樣,一層一層地進行,16ms刷一次。這樣就會造成,圖層覆蓋的現象,即無用的圖層還被繪製在底層,造成不必要的浪費
這裏寫圖片描述

GPU過度繪製的幾種情況

  1. 自定義控件中,onDraw方法做了過多重複繪製
  2. 佈局層次太深,重疊性太強。用戶看不到的區域GPU也會渲染,導致耗時增加

過度繪製查看工具
在手機端的開發者選項裏,裏面有個調試GPU過度繪製工具
點擊以後會發現你的屏幕變得紅紅綠綠的,這些顏色就代表了特定部分過度繪製的程度

  • 藍色:過度繪製一次(無過度繪製)
  • 淡綠:過度繪製兩次
  • 淡紅:過度繪製三次
  • 深紅:過度繪製四次
    我們優化的目的就是儘量減少紅色,看到更多藍色的區域。

這裏寫圖片描述

可以看到,這裏的RecyclerView佈局列表項重複繪製了四次,導致畫面十分卡頓。

通過分析,我們發現問題有以下幾個:

  1. 背景和主題衝突,重複繪製兩次。解決辦法,設置主題爲null。之後我們發現背景由綠色變爲了藍色,減少了一層繪製
  2. imageView也設置了一個background,導致過度繪製,解決辦法,將這個background去掉。之後圖片也由淡紅變成了綠色。
  3. RecyclerView的item項中多了一層不必要的LinearLayout,將其刪除,我們的過度繪製又減少了一層。

其實還有更多方法來減少過度繪製,優化我們的性能。

  • ListView,RecyclerView列表項中避免使用LinearLayout多重嵌套,儘量用一個RelativeLayout。
  • 可以在TextView中加上drawableLeft來設置圖片,避免使用imageView+TextView的方式。

    因爲所有的佈局解析都要消耗cpu的計算性能,所以Layout並不是越多越好。

五、自定義View的優化

自定義View優化的重點主要在Canvas上,例如出現幾個View互相層疊時(卡牌式),可以通過裁剪畫布防止過度繪製
這裏寫圖片描述

六、使用Hierarchy Viewer 優化佈局

在Android Studio 的Android Device Monitor中直接打開這個工具這裏寫圖片描述
這裏寫圖片描述

然後打開我們要檢測的app,然後
這裏寫圖片描述

之後可以看到我們的View樹,
這裏寫圖片描述

看到這個簡直崩潰了,太多了。
我們看到視圖中幾乎所有節點都有三個點,顏色也各不相同
這三個點也是代表着View的Measure, Layout和Draw。
不同顏色意味着不同的速度:

綠: 表示該View的此項性能比該View Tree中超過50%的View都要快;例如,代表Measure的是綠點,意味着這個視圖的測量時間快於樹中的視圖對象的50%。
黃: 表示該View的此項性能比該View Tree中超過50%的View都要慢;
紅: 表示該View的此項性能是View Tree中最慢的。

這裏寫圖片描述

可以很清楚的分辨清不同View的性能以及Measure, Layout和Draw的時間,通過紅色的View節點進去查看,佈局是否有不合理之處。

優化方式

  • Hierarchy Viewer View樹中只有一個子節點且id沒有被其他地方使用的佈局可以去掉
  • 減少不必要嵌套
  • 使用merge標籤避免與父容器重疊。
  • include 複用佈局,並且複用的佈局可以在GPU裏可以高效緩存