hwui大概流程和FrameInfo統計資訊
Android hwui硬體加速從3.0版本開始引入到7.0已經非常複雜,這裡總結下大致的流程和原理
如果input的down事件到來,有些view接收到事件要求系統重繪就會呼叫到ViewRootImpl.scheduleTraversals()方法如下程式碼
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null );
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
首先新增一個Choreographer.CALLBACK_TRAVERSAL回撥函式,等待app vsync(vyns訊號虛化)訊號到來處理,
注意第一次添加回調會請求surfaceflinger傳送app vsync事件給當前應用,之後呼叫scheduleConsumeBatchedInput()
void scheduleConsumeBatchedInput() {
if (!mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = true;
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
mConsumedBatchedInputRunnable, null);
}
}
新增一個CALLBACK_INPUT回撥用於在 app vsynv到來後處理批量input事件.
之後vsync事件到來,會執行到Choreographer.doFrame()方法,下面是關鍵步驟
ry {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
分如下幾步處理
1.處理input回撥處理事件,導致檢視變化
2.處理同步動畫
處理檢視變化
處理屬性動畫
對於第3步會更新檢視
這裡要補充的背景知識就是硬體加速的知識,hwui裡增加了一個執行緒render thread,會不斷輪訓新的繪製任務,app main執行緒在執行draw函式的時候
會生成一系列的繪製命令,(另外比較重要的是bitmap資源,如呼叫canvas.drawbitmap就要儲存這個要繪製的圖片). 將這些繪製命令儲存好後最終會
交給render thread去繪製. 交給render thread處理其實就是向它的任務佇列新增訊息,然後在threadloop中處理,
處理的過程要和main執行緒進行資料同步,在同步的過程中main執行緒是被阻塞的,防止main執行緒返回去後修改ui. 同步中最重要的一個步驟就是上傳bitmap
到gpu紋理.同步完成後就可以喚醒主執行緒去做事情了(這裡也並不一定直接喚醒,有時候要等到render thread渲染完成才會喚醒),
之後render thread就可以義無反顧的進行opengl繪製了,繪製完成要呼叫swapbuffer去把buffer交給
surfaceflinger進行合成(swapbuffer的過程會呼叫queueBuffer給surfaceflinger資料,同時也會呼叫dequeueBuffer獲取新的buffer用於下一幀繪製).
交換完buffer就是一些清理工作,之後進入睡眠等待下一次任務. 整個程式碼流程很長,也很複雜不詳細列舉程式碼.
對於先不喚醒main thread的情況就是上傳的紋理太多超出了限制(預設一個應用做多70M),太多的意思是同時使用的紋理資料,不使用的可以釋放(lrucache)
另外對於每一幀資料,hwui會統計如下資訊,這裡做出解釋
enum class FrameInfoIndex {
Flags = 0,
IntendedVsync,
Vsync,
OldestInputEvent,
NewestInputEvent,
HandleInputStart,
AnimationStart,
PerformTraversalsStart,
DrawStart,
// End of UI frame info
SyncQueued,
SyncStart,
IssueDrawCommandsStart,
SwapBuffers,
FrameCompleted,
DequeueBufferDuration,
QueueBufferDuration,
// Must be the last value!
// Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
NumIndexes
};
Flags
enum { WindowLayoutChanged = 1 << 0, RTAnimation = 1 << 1, SurfaceCanvas = 1 << 2, SkippedFrame = 1 << 3,};
支援上四種flags 其中WindowLayoutChanged代表windowlayout發生變化(這裡會多用wms),SkippedFrame跳幀
其他的還沒分析清楚IntendedVsync app_vsync的時間
Vsync 開始處理vync事件的時間
OldestInputEvent 如上處理批量事件中最老的一個inputEvent的時間
NewestInputEvent 最新事件
HandleInputStart mainthread開始處理input事件的時間
AnimationStart mainthread開始處理動畫的時間
PerformTraversalsStart mainthread開始遍歷檢視的時間
DrawStart mainthread開始執行draw函式的時間
SyncQueued 新增事件到ThreadRender的時間
SyncStart renderthread開始同步main thread資料的的時間
IssueDrawCommandsStart renderThread開始繪製的時間
SwapBuffers renderThread開始交換buffer的時間
FrameCompleted renderThread 完成交換buffer的時間
DequeueBufferDuration renderThread交換buffer中dequeueBuffer花費的時間
QueueBufferDuration renderThread queueBuffer的時間
DequeueBufferDuration 和 QueueBufferDuration如果時間比較長的話系統卡段的可能性比較達
如果發生windowlayout的話PerformTraversalsStart和DrawStart 之間間隔比較長的話有可能是系統卡頓引起