Choreographer的作用
1丧慈、負(fù)責(zé)接收和處理 App 的各種更新消息和回調(diào)丈牢,等到 Vsync 到來的時候統(tǒng)一處理删豺。比如集中處理 Input(主要是 Input 事件的處理) 立砸、Animation(動畫相關(guān))戈咳、Traversal(包括 measure网沾、layout癞蚕、draw 等操作) ,判斷卡頓掉幀情況辉哥,記錄 CallBack 耗時等
2桦山、負(fù)責(zé)請求和接收 Vsync 信號。接收 Vsync 事件回調(diào)(通過 FrameDisplayEventReceiver.onVsync )醋旦;請求 Vsync(FrameDisplayEventReceiver.scheduleVsync) .
Choreographer初始化
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;//1
mHandler = new FrameHandler(looper);//2
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;//3
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];//4
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
首先需要一個Looper,然后通過Looper來創(chuàng)建一個Handler對象恒水。載創(chuàng)建一個mDisplayEventReceiver對象,主要是處理Vsync信號的回調(diào)和請求饲齐。最后初始化一個回調(diào)隊列钉凌。
FrameHandler
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);//1
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();//2
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);//3
break;
}
}
}
1表示開始下一幀的渲染操作
2表示請求Vsync
3表示處理callback
FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
....
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
該類繼承DisplayEventReceiver,實現(xiàn)其兩個方法為onVsync()和run()捂人,onVsync()主要是處理Vsync信號的回調(diào)御雕,然后將消息設(shè)置成異步,通過我們的FrameHandler發(fā)送出去滥搭。run()則是調(diào)用doFrame()酸纲,表示我們接收到Vsync信號之后,開始下一幀的渲染瑟匆。
onVsync()方法中只是使用mHandler發(fā)送消息到MessageQueue中闽坡,不一定是立刻執(zhí)行,如果MessageQueue中前面有較為耗時的操作,那么就要等完成无午,才會執(zhí)行本次的doFrame()媒役。
前面說了,F(xiàn)rameDisplayEventReceiver是請求和回調(diào)宪迟,那請求時在哪里了酣衷?
@UnsupportedAppUsage
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
真正的請求是在DisplayEventReceiver類中的scheduleVsync(),里面調(diào)用的是一個native方法來進(jìn)行請求Vsync信號次泽。DisplayEventReceiver 初始化完成后穿仪,Choreographer 與 SurfaceFlinger 的 Vsync 信號傳遞通道就建立好了,后續(xù)有 Vsync 信號到來時意荤,就可以沿著這條通道啊片,一路回調(diào)到 Java 層的 Choreographer.FrameDisplayEventReceiver.onVsync 方法。
callback類型
//輸入事件玖像,首先執(zhí)行
public static final int CALLBACK_INPUT = 0;
//動畫紫谷,第二執(zhí)行
public static final int CALLBACK_ANIMATION = 1;
//繪制,第三執(zhí)行
public static final int CALLBACK_TRAVERSAL = 2;
//提交捐寥,最后執(zhí)行笤昨,
public static final int CALLBACK_COMMIT = 3;
callback類型四種,其執(zhí)行的順序為0-》1-》2-》3
執(zhí)行流程
那什么時候會用到Choreographer握恳,通過調(diào)用invalidate()或者requestLayout()方法的時候瞒窒,也就是我們需要UI繪制的時候,需要用到Choreographer乡洼。
上面的兩個方法最終都會走到scheduleTraversals()崇裁。
scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
首先會設(shè)置一個同步屏障,然后調(diào)用postCallback()方法.postCallback()方法中繼續(xù)調(diào)用postCallbackDelayed()束昵,最終會調(diào)用postCallbackDelayedInternal()方法拔稳。
postCallbackDelayedInternal()
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
如果dueTime<=當(dāng)前時間,則會去請求Vsync信號妻怎,否則通過handler發(fā)送一條異步消息到隊列中壳炎。
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
1、如果系統(tǒng)未開啟 VSYNC 機(jī)制逼侦,此時直接發(fā)送 MSG_DO_FRAME 消息到 FrameHandler匿辩,此時直接執(zhí)行 doFrame 方法。
2榛丢、Android 4.1 之后系統(tǒng)默認(rèn)開啟 VSYNC铲球,在 Choreographer 的構(gòu)造方法會創(chuàng)建一個 FrameDisplayEventReceiver,scheduleVsyncLocked 方法將會通過它申請 VSYNC 信號晰赞。
3稼病、isRunningOnLooperThreadLocked 方法选侨,其內(nèi)部根據(jù) Looper 判斷是否在原線程,否則發(fā)送消息到 FrameHandler然走。最終還是會調(diào)用 scheduleVsyncLocked 方法申請 VSYNC 信號援制。
請求Vsync信號成功之后,就會走onVsync()回調(diào)芍瑞。onVsync()中晨仑,將接收器本身作為runnable傳入異步消息msg,并使用mHandler發(fā)送msg拆檬,最終執(zhí)行的就是doFrame()方法洪己。
doFrame()
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
在doFrame方法中,主要做了以下的事:
1竟贯、計算掉幀邏輯
Choreographer.doFrame 的掉幀檢測比較簡單答捕,Vsync 信號到來的時候會標(biāo)記一個 start_time ,執(zhí)行 doFrame 的時候標(biāo)記一個 end_time 屑那,這兩個時間差就是 Vsync處理時延拱镐,也就是掉幀。
這種方法計算的掉幀齐莲,是前一幀的掉幀情況痢站,而不是這一幀的掉幀情況磷箕,這個計算方法是有缺陷的选酗,會導(dǎo)致有的掉幀沒有被計算到
2、記錄幀繪制信息
Choreographer 中 FrameInfo 來負(fù)責(zé)記錄幀的繪制信息岳枷,doFrame 執(zhí)行的時候芒填,會把每一個關(guān)鍵節(jié)點(diǎn)的繪制時間記錄下來,我們使用 dumpsys gfxinfo 就可以看到空繁。
3殿衰、執(zhí)行 CALLBACK_INPUT、CALLBACK_ANIMATION盛泡、CALLBACK_TRAVERSAL闷祥、CALLBACK_COMMIT
最后執(zhí)行的是c.run(frameTimeNanos);
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
最后調(diào)用的是run();
現(xiàn)在我們再來看scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
((Runnable)action).run()其實執(zhí)行的是mTraversalRunnable傲诵。
mTraversalRunnable()
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();//1
}
}
從1可以知道凯砍,熟悉view的繪制流程的就知道在performTraversals()中會去執(zhí)行Measure(),Layout(),Draw(),就這樣就形成了一個閉環(huán)拴竹。
小結(jié)
1悟衩、Choreographer 是線程單例的,而且必須要和一個 Looper 綁定栓拜,因為其內(nèi)部有一個 Handler 需要和 Looper 綁定座泳,一般是 App 主線程的 Looper 綁定惠昔。
2、DisplayEventReceiver 是一個 abstract class挑势,其 JNI 的代碼部分會創(chuàng)建一個IDisplayEventConnection 的 Vsync 監(jiān)聽者對象镇防。這樣,來自 AppEventThread 的 VSYNC 中斷信號就可以傳遞給 Choreographer 對象了潮饱。當(dāng) Vsync 信號到來時营罢,DisplayEventReceiver 的 onVsync 函數(shù)將被調(diào)用。
3饼齿、DisplayEventReceiver 還有一個 scheduleVsync 函數(shù)饲漾。當(dāng)應(yīng)用需要繪制UI時,將首先申請一次 Vsync 中斷缕溉,然后再在中斷處理的 onVsync 函數(shù)去進(jìn)行繪制考传。
4、Choreographer 定義了一個 FrameCallback interface证鸥,每當(dāng) Vsync 到來時僚楞,其 doFrame 函數(shù)將被調(diào)用。這個接口對 Android Animation 的實現(xiàn)起了很大的幫助作用枉层。以前都是自己控制時間泉褐,現(xiàn)在終于有了固定的時間中斷。
5鸟蜡、Choreographer 的主要功能是膜赃,當(dāng)收到 Vsync 信號時,去調(diào)用使用者通過 postCallback 設(shè)置的回調(diào)函數(shù)揉忘。目前一共定義了四種類型的回調(diào)跳座,它們分別是:
CALLBACK_INPUT : 處理輸入事件處理有關(guān)
CALLBACK_ANIMATION : 處理 Animation 的處理有關(guān)
CALLBACK_TRAVERSAL : 處理和 UI 等控件繪制有關(guān)
CALLBACK_COMMIT : 處理 Commit 相關(guān)回調(diào)
6、CALLBACK_INPUT 泣矛、CALLBACK_ANIMATION 會修改 view 的屬性疲眷,所以要先于 CALLBACK_TRAVERSAL 執(zhí)行