第一守則:不要解決非瓶頸部分
除非你很閒,否則CP值很低。Rendering通常是最大效能瓶頸
在android上安排UI或是畫圖,若是沒有理解Android的繪圖流程,很容易造成畫面效能低落。如果要達成流暢的60 fps,我們需要在每16 ms的time frame中完成每一個frame的繪製:
算法就是螢幕refresh rate = 60Hz = 60 次/sec,所以相當於每 1/60 = 0.016秒 = 16ms refresh一次。
如果你的drawing無法在16 ms中完成的話,當然就無法達成60 fps,此時就稱為drop frame:
Rendering Pipeline
繪製一個frame需要兩個component去計算,一個是CPU,主要負責項目如下:layout hierarchy過於龐大深度,會造成layout重複計算,是主要原因。
GPU的主要負責項目只有幫pixel上色 (rasterization,非常耗時):
GPU最可能發生的效能問題就是overdraw,重複畫在同一個區域。
Android UI & GPU
真正做rasterization的是GPU,但是CPU也扮演著把UI object計算過,並且透過openGL ES上傳到GPU memory的角色,這兩個動作都是非常耗時的:所以最好要減少計算UI Object的時間,以及減少上傳到GPU的次數。
Honeycomb 之後的android使用GPU rendering不需要programmer去操心了,除了overdraw的問題。
Overdraw problem
overdraw定義:一個frame中,某個pixel被重複畫了的次數。這可能發生在重疊的view,但是CPU和GPU不能罵他笨,他們只是忠實的重現programmer的意志:上面這樣堆疊主要是浪費GPU 無謂rasterization的時間。
Android 使用者開發選項有一個 “show GPU overdraw”,打開後可以檢視目前螢幕上的app的overdraw程度:
可以藉由重新layout xml來調教:
fix overdraw problem
1. 我們的activity如果有apply theme,則他會有一個background color被指定要繪製,這可能跟actitvy本身的content layout的background color不一樣,所以壓在layout下面,看不到,此時應該要把這個theme本身的background 拿掉:這樣可以減少一次重繪。
2. 檢查所有重疊view的background,只留下你真正需要的background設定。
3. 調整邏輯,某些時候重複繪製是可以靠程式邏輯去避免的。
Clipping
Android UI rendering基本上會自己計算那些UI 部分是重疊的而不去繪製被壓在下面看不見得部分,但是當我們自己extends View,並且override onDraw()的時候,等於就放棄了這個系統的判斷,所以要跟Android明確定義出visible rect:這樣的動作稱為clipping。
舉例來說,以下是一個custom view:
可以看到onDraw如下:
卡片有重疊部分還是重畫了,沒有做任何處理。
現在來看使用clipRect要怎麼改:
所以基本上clipRect是改變canvas state,設定一個只能在clipRect,則rendering system只會畫在這個clipRect中間的顏色。但是由於每張卡片的clipRect都不一樣,所以要記得canvas.save()來儲存canvas原本的state,之後繪製目前clipRect之後還要再restore到沒有設定clipRect的state。
當然最後一張卡片由於是沒有被任何其他卡片覆蓋住的,所以不用設定clipRect,直接畫出整張卡片即可。
改善結果如下:
CPU optimization
CPU負責把layout xml file轉換成openGL需要的display list,如此GPU才知道要畫些什麼:所以這個display list會被cache在GPU memory,任何只是單純改變屬性 (這邊定義不太清楚)的狀況,都不會改變這個display list,所以這個display list就是這個Button的繪製藍圖。
但是一旦Button的任何pixel內容改變了,例如多出陰影或是按下的動畫等等,這個view就需要被invalidate,告訴CPU要重新製作display list,所以上面的流程都要重新跑一次,耗費了CPU與GPU的資源。
以scale button為例:
scaling會觸發Measure過程,因為系統要知道你的最後大小與位置,另外也有可能影響到其他UI component的位置與大小,所以有可能觸發Layout過程,接下來就是invalidate被影響view去重新計算display list交給GPU去重畫。
注意Measure和Layout會讓Android重新評估所有受影響的UI components,這個過程耗費的資源和整個xml定義的view hierarchy息息相關,所以調整view hierarchy是重要的,不要設計無謂龐大的hierarchy。
Hierarchy Viewer
主要目的:1. 用android rendering system的視角來看layout hierarchy
2. 提供hierarchy扁平化的可能性,加速rendering和memory consumption。
(不過這個工具已經obsolete,這邊不做筆記了)
以下是兩個一模一樣的視覺呈現,但是layout hierarchy不一樣,可以看到上面那個row的階層比較深,performance的差異如下:
沒有留言:
張貼留言