1. 程式人生 > >Android優化筆記(一)——渲染篇(原理)

Android優化筆記(一)——渲染篇(原理)

顯示機制和重新整理機制

這裡寫圖片描述
Android的顯示系統是一個典型的顯示系統,它由CPU,GPU,Display組成,CPU負責計算資料,把計算好資料交給GPU,GPU會對圖形資料進行渲染,渲染好後放到buffer裡存起來,然後Display負責把buffer裡的資料呈現到螢幕上。

對於 Android 而言,第一個步驟:CPU 計算螢幕資料指的也就是 View 樹的繪製過程,也就是 Activity 對應的檢視樹從根佈局 DecorView 開始層層遍歷每個 View,分別執行測量、佈局、繪製三個操作後得到Buffer快取資料,再通過SurfaceFlinger把資料渲染到螢幕上。

SurfaceFlinger 是一個獨立的Service, 它接收所有Window的Surface作為輸入,根據ZOrder, 透明度,大小,位置等引數,計算出每個Surface在最終合成影象中的位置,然後交由HWComposer或OpenGL生成最終的顯示Buffer, 然後顯示到特定的顯示裝置上。

系統由HwComposer模組啟動SurfaceFlinger程序,VSYNC訊號是由HWC產生的。Choreographer則是接收VSYNC訊號的類,每次接收VSYNC訊號後,再通知View執行dispatchDraw方法。

Android4.1引入VSYNC機制,Android系統每隔16ms發出VSYNC訊號(vertical synchronization –場掃描同步,場同步,垂直同步),觸發對UI進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味著程式的大多數操作都必須在16ms(1000/60=16.67ms)內完成。

卡頓原理

如果你的某個操作花費時間是24ms,系統在得到VSYNC訊號的時候就無法進行正常渲染,這樣就發生了丟幀現象。那麼使用者在32ms內看到的會是同一幀畫面。如果佔用了n個16ms,則說明丟了n幀。

判斷標準

關於FPS,FPS獲取原理:手機螢幕顯示的內容是通過Android系統的SurfaceFLinger類,把當前系統裡所有程序需要顯示的資訊合成一幀,然後提交到螢幕進行顯示。FPS判斷卡頓是不準確的,系統其它程序也會影響FPS,你的APP繪製完成後FPS理論為0.用FPS來判斷APP卡頓是不準確的,實測APP放著不動FPS為1,其他程序也會重新整理介面,如狀態列。

Android系統在流暢的情況下繪幀速度是60幀/s(即:16.7ms一幀)。當繪幀間隔超過一定時長,我們就可以說此時掉幀了,也就會造成使用者直接感官的卡頓。此模組可以統計一秒內繪幀次數(即:流暢度SM),並對丟幀的原因進行程式碼定位。

首先Android的幀繪製流程是:CPU主執行緒影象處理->GPU進行光柵化->顯示幀。APP產生掉幀的情況大多是由“CPU主執行緒影象處理”這一步超負載引起的,所以我們思考如何去監控主執行緒繪製情況。要檢測CPU繪製幀的時間,就必須找到那個呼叫View.dispatchDraw的類,Choreographer類就是那個接受系統垂直同步訊號,在每次接受時同步訊號觸發View的Input、Animation、Draw等操作。

所以我們可以向Choreographer類中加入自己的Callback來冒充View的Callback(Choreographer.FrameCallback中的doFrame通知我們下一幀的到來,再做簡單的計數統計),通過此Callback我們可以獲得View繪製的時間、可以統計一秒內幀繪製的能力(後面把此值稱作“流暢值SM”,它能直觀的代表當前時間段的流暢度)。之所以不用FPS來代表當前流暢度,是因為Android系統預設在前臺頁面靜止時,FPS可能為0,FPS低無法直接代表當前處於卡頓。

可以從兩個方向來分析流暢度(SM)資料:
1)單次大卡頓:當兩次繪幀間隔大於70ms,相當於丟了4幀以上,建議開發人員對耗時的程式碼進行非同步或拆分。
2)低流暢值區間:流暢值低於40幀/s的區間,導致低流暢值區間出現的原因有兩類:“單次大卡頓”“連續小卡頓”,建議開發人員針對不同的場景進行優化。

卡頓原因

1、人為在UI執行緒中做輕微耗時操作,導致UI執行緒卡頓;
2、佈局Layout過於複雜,無法在16ms內完成渲染;
3、同一時間動畫執行的次數過多,導致CPU或GPU負載過重;
4、View過度繪製,導致某些畫素在同一幀時間內被繪製多次,從而使CPU或GPU負載過重;
5、View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;
6、記憶體頻繁觸發GC過多(同一幀中頻繁建立記憶體),導致暫時阻塞渲染操作;
7、冗餘資源及邏輯等導致載入和執行緩慢;
8、臭名昭著的ANR;

大多數使用者感知到的卡頓等效能問題的最主要根源都是因為渲染效能。(Google官方說的)

關於Choreographer

控制外部輸入事件處理,動畫執行,UI變化,以及提交執行都是在同一個類中做的處理,即是Choreographer。

在Choreographer物件中有四條連結串列,分別儲存著待處理的輸入事件,待處理的動畫事件,待處理的遍歷事件,以及待處理的提交時間。

每次執行的時候,Choreographer會根據當前的時間,只處理事件連結串列中最後一個事件,當有耗時操作在主執行緒時,事件不能及時執行,就會出現所謂的“跳幀”,“卡頓”現象。

Choreographer的共有方法postCallback(callbackType, Object)是往事件連結串列中放事件的方法。而doFrame()是消耗這些事件的方法。

Thanks