分析Android Choreographer源碼
目前大部分手機(jī)都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系統(tǒng)為了配合屏幕的刷新頻率,將 Vsync 的周期也設(shè)置為 16.6 ms,每個(gè) 16.6 ms , Vsync 信號(hào)喚醒 Choreographer 來(lái)做 App 的繪制操作,這就是引入 Choreographer 的主要作用。了解 Choreographer 還可以幫助 App 開(kāi)發(fā)者知道程序每一幀運(yùn)行的基本原理,也可以加深對(duì) Message、Handler、Looper、MessageQueue、Measure、Layout、Draw 的理解
二、主線程運(yùn)行機(jī)制的本質(zhì)在介紹 Choreographer 之前,我們先理一下 Android 主線程運(yùn)行的本質(zhì),其實(shí)就是 Message 的處理過(guò)程,我們的各種操作,包括每一幀的渲染,手勢(shì)操作 ,都是通過(guò) Message 的形式發(fā)給主線程的 MessageQueue ,MessageQueue 處理完消息繼續(xù)等下一個(gè)消息,如下圖所示
MethodTrace 圖示
Systrace 圖示
可以發(fā)現(xiàn),每一幀時(shí)間都是固定的。所以一旦一個(gè)Message 的處理時(shí)間超過(guò)了 16.6ms 就會(huì)引起卡頓。
三、Choreographer 簡(jiǎn)介Choreographer 扮演 Android 渲染鏈路中承上啟下的角色
1.承上:負(fù)責(zé)接收和處理 App 的各種更新消息和回調(diào),等到 Vsync 到來(lái)的時(shí)候統(tǒng)一處理。比如集中處理 Input(主要是 Input 事件的處理) 、Animation(動(dòng)畫相關(guān))、Traversal(包括 measure、layout、draw 等操作) ,判斷卡頓掉幀情況,記錄 CallBack 耗時(shí)等
2.啟下:負(fù)責(zé)請(qǐng)求和接收 Vsync 信號(hào)。接收 Vsync 事件回調(diào)(通過(guò) FrameDisplayEventReceiver.onVsync );請(qǐng)求 Vsync(FrameDisplayEventReceiver.scheduleVsync) .
從上面可以看出來(lái), Choreographer 擔(dān)任的是一個(gè)工具人的角色,他之所以重要,是因?yàn)橥ㄟ^(guò)Choreographer + SurfaceFlinger + Vsync + TripleBuffer這一套從上到下的機(jī)制,保證了 Android App 可以以一個(gè)穩(wěn)定的幀率運(yùn)行(目前大部分是 60fps),減少幀率波動(dòng)帶來(lái)的不適感。
3.1、Choreographer 的工作流程Choreographer 初始化SurfaceFlinger 的 appEventThread 喚醒發(fā)送 Vsync ,Choreographer 回調(diào) FrameDisplayEventReceiver.onVsync , 進(jìn)入 Choreographer 的主處理函數(shù) doFrame
初始化 FrameHandler ,綁定 Looper 初始化 FrameDisplayEventReceiver ,與 SurfaceFlinger 建立通信用于接收和請(qǐng)求 Vsync 初始化 CallBackQueuesChoreographer.doFrame 計(jì)算掉幀邏輯
Choreographer.doFrame 處理 Choreographer 的第一個(gè) callback : input
Choreographer.doFrame 處理 Choreographer 的第二個(gè) callback : animation
Choreographer.doFrame 處理 Choreographer 的第三個(gè) callback : insets animation
Choreographer.doFrame 處理 Choreographer 的第四個(gè) callback : traversalChoreographer.doFrame 處理 Choreographer 的第五個(gè) callback : commit ?traversal-draw 中 UIThread 與 RenderThread 同步數(shù)據(jù)
RenderThread 處理繪制數(shù)據(jù),真正進(jìn)行渲染
將渲染好的 Buffer swap 給 SurfaceFlinger 進(jìn)行合成
四、Choreographer 源碼分析4.1、Choreographer 的單例初始化// Thread local storage for the choreographer.private static final ThreadLocal<Choreographer> sThreadInstance =new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() {Looper looper = Looper.myLooper();if (looper == null) { throw new IllegalStateException('The current thread must have a looper!');}Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);if (looper == Looper.getMainLooper()) { mMainInstance = choreographer;}return choreographer; }};
這里采用的是ThreadLocal 來(lái)構(gòu)造單例,這樣每個(gè)線程都會(huì)有一個(gè)屬于自己的 choreographer 實(shí)例。
接下去看choreographer 的構(gòu)造函數(shù)
private Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mHandler = new FrameHandler(looper); // 這里可以發(fā)現(xiàn)只有在為 true 的時(shí)候才會(huì)使用 vsync mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; mLastFrameTimeNanos = Long.MIN_VALUE; // 每一幀的間隔是根據(jù)刷新頻率來(lái)的 mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; // 給每一種回調(diào)類型都創(chuàng)建了一個(gè)隊(duì)列 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));}
這里做了幾個(gè)初始化操作,根據(jù)Looper對(duì)象生成,Looper和線程是一對(duì)一的關(guān)系,對(duì)應(yīng)上面說(shuō)明里的每個(gè)線程對(duì)應(yīng)一個(gè)Choreographer。
初始化FrameHandler。接收處理消息。 初始化FrameDisplayEventReceiver。FrameDisplayEventReceiver用來(lái)接收垂直同步脈沖,就是VSync信號(hào),VSync信號(hào)是一個(gè)時(shí)間脈沖,一般為60HZ,用來(lái)控制系統(tǒng)同步操作,怎么同ChoreoGrapher一起工作的,將在下文介紹。 初始化mLastFrameTimeNanos(標(biāo)記上一個(gè)frame的渲染時(shí)間)以及mFrameIntervalNanos(幀率,fps,一般手機(jī)上為1s/60)。 初始化CallbackQueue,callback隊(duì)列,將在下一幀開(kāi)始渲染時(shí)回調(diào)。接下去看看 FrameHandler 和 FrameDisplayEventReceiver 的結(jié)構(gòu)。
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);break; case MSG_DO_SCHEDULE_VSYNC:doScheduleVsync();break; case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);break;} }}
看上面的代碼,就是一個(gè)簡(jiǎn)單的Handler。處理3個(gè)類型的消息。
MSG_DO_FRAME:開(kāi)始渲染下一幀的操作 MSG_DO_SCHEDULE_VSYNC:請(qǐng)求 Vsync 信號(hào) MSG_DO_SCHEDULE_CALLBACK:請(qǐng)求執(zhí)行 callback下面再細(xì)分一下,分別詳細(xì)看一下這三個(gè)步驟是怎么實(shí)現(xiàn)的。
4.2、FrameDisplayEventReceiverprivate final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {super(looper, vsyncSource); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC // for the internal display implicitly. @Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {// Post the vsync event to the Handler.// The idea is to prevent incoming vsync events from completely starving// the message queue. If there are no messages in the queue with timestamps// earlier than the frame time, then the vsync event will be processed immediately.// Otherwise, messages that predate the vsync event will be handled first.long now = System.nanoTime();if (timestampNanos > now) { Log.w(TAG, 'Frame time is ' + ((timestampNanos - now) * 0.000001f) + ' ms in the future! Check that graphics HAL is generating vsync ' + 'timestamps using the correct timebase.'); timestampNanos = now;}if (mHavePendingVsync) { Log.w(TAG, 'Already have a pending vsync event. There should only be ' + 'one at a time.');} else { mHavePendingVsync = true;}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); }}
FrameDisplayEventReceiver 繼承自 DisplayEventReceiver,同時(shí)也實(shí)現(xiàn)了Runnable 接口,是處于 Choreographer 中的私有內(nèi)部類。當(dāng)接收到底層的 VSync 信號(hào)開(kāi)始處理 UI 過(guò)程。VSync 信號(hào)由 SurfaceFlinger 實(shí)現(xiàn)并定時(shí)發(fā)送。FrameDisplayEventReceiver 收到信號(hào)后,調(diào)用 onVsync 方法組織消息發(fā)送到主線程處理。這個(gè)消息主要內(nèi)容就是 run 方法里面的 doFrame 了,這里 mTimestampNanos 是信號(hào)到來(lái)的時(shí)間參數(shù)。
那么 FrameDisplayEventReceiver 是通過(guò)什么方式在 Vsync 信號(hào)到來(lái)的時(shí)候回調(diào) onVsync 呢?答案是 FrameDisplayEventReceiver 的初始化的時(shí)候,最終通過(guò)監(jiān)聽(tīng)文件句柄的形式,其對(duì)應(yīng)的初始化流程如下
// android/view/Choreographer.javaprivate Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; ......}// android/view/Choreographer.javapublic FrameDisplayEventReceiver(Looper looper, int vsyncSource) { super(looper, vsyncSource);}// android/view/DisplayEventReceiver.javapublic DisplayEventReceiver(Looper looper, int vsyncSource) { ...... mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, vsyncSource);}
nativeInit 后續(xù)的代碼可以自己跟一下,可以對(duì)照這篇文章和源碼,由于篇幅比較多,這里就不細(xì)說(shuō)了。
簡(jiǎn)單來(lái)說(shuō),F(xiàn)rameDisplayEventReceiver 的初始化過(guò)程中,通過(guò) BitTube (本質(zhì)是一個(gè) socket pair),來(lái)傳遞和請(qǐng)求 Vsync 事件,當(dāng) SurfaceFlinger 收到 Vsync 事件之后,通過(guò) appEventThread 將這個(gè)事件通過(guò) BitTube 傳給 DisplayEventDispatcher ,DisplayEventDispatcher 通過(guò) BitTube 的接收端監(jiān)聽(tīng)到 Vsync 事件之后,回調(diào) Choreographer.FrameDisplayEventReceiver.onVsync ,觸發(fā)開(kāi)始一幀的繪制。
如下圖
FrameHandler 和 FrameDisplayEventReceiver 是怎么工作的呢?ChoreoGrapher 的總體流程圖如下圖(拷貝的圖片):
以上是總體的流程圖:
1.PostCallBack orpostFrameCallback發(fā)起添加回調(diào),這個(gè)FrameCallBack將在下一幀被渲染時(shí)執(zhí)行。
2.AddToCallBackQueue,將 FrameCallBack 添加到回調(diào)隊(duì)列里面,等待時(shí)機(jī)執(zhí)行回調(diào)。每種類型的callback按照設(shè)置的執(zhí)行時(shí)間(dueTime)順序排序分別保存在一個(gè)單鏈表中。
3.判斷 FrameCallBack設(shè)定的執(zhí)行時(shí)間是否在當(dāng)前時(shí)間之后,若是,發(fā)送 MSG_DO_SCHEDULE_CALLBACK 消息到主線程,安排執(zhí)行doScheduleCallback,安排執(zhí)行CallBack。否則直接跳到第4步。
4.執(zhí)行 scheduleFrameLocked,安排執(zhí)行下一幀。
5.判斷上一幀是否已經(jīng)執(zhí)行,若未執(zhí)行,當(dāng)前操作直接結(jié)束。若已經(jīng)執(zhí)行,根據(jù)情況執(zhí)行以下6、7步。
6.若使用垂直同步信號(hào)進(jìn)行同步,則執(zhí)行7.否則,直接跳到9。
7.若當(dāng)前線程是UI線程,則通過(guò)執(zhí)行scheduleVsyncLocked請(qǐng)求垂直同步信號(hào)。否則,送MSG_DO_SCHEDULE_VSYNC消息到主線程,安排執(zhí)行doScheduleVsync,在主線程調(diào)用scheduleVsyncLocked。
8.收到垂直同步信號(hào),調(diào)用FrameDisplayEventReceiver.onVsync(),發(fā)送消息到主線程,請(qǐng)求執(zhí)行doFrame。
9.執(zhí)行doFrame,渲染下一幀。
主要的工作在 doFrame 中,接下來(lái)我們具體看看 doFrame 函數(shù)都干了些什么。從名字看很容易理解 doFrame 函數(shù)就是開(kāi)始進(jìn)行下一幀的顯示工作。好了以下源代碼又來(lái)了,我們一行一行分析一下吧。
4.4、doFrame@UnsupportedAppUsagevoid doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { // 為false, 說(shuō)明還未開(kāi)始if (!mFrameScheduled) { return; // no work to do}if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) { mDebugPrintNextFrameTimeDelta = false; Log.d(TAG, 'Frame time delta: ' + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + ' ms');}long intendedFrameTimeNanos = frameTimeNanos;startNanos = System.nanoTime(); // 計(jì)算當(dāng)前時(shí)間與 vsync 信號(hào)的時(shí)間差final long jitterNanos = startNanos - frameTimeNanos; // 說(shuō)明出現(xiàn)掉幀情況,注意只有 jitterNanos 大于 16.6 ms 才說(shuō)明掉幀,否則只是輕微的延遲。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.'); }// 當(dāng)發(fā)生掉幀后,需要計(jì)算被耽誤的時(shí)間。比如處理了 36.6ms, 一個(gè)周期是 16.6 ms, 相當(dāng)于延遲了 3.4 ms 執(zhí)行 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.'); } // 修正當(dāng)前幀的時(shí)間 = 開(kāi)始時(shí)間 - 耽誤時(shí)間 frameTimeNanos = startNanos - lastFrameOffset;} // 當(dāng)前時(shí)間小于前一幀時(shí)間,不執(zhí)行操作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.'); }// 直接請(qǐng)求下一個(gè) vsync 信號(hào) scheduleVsyncLocked(); return;} // 大于 1 說(shuō)明采用的默認(rèn)幀數(shù)的一半,因此需要根據(jù)時(shí)間間隔來(lái)判斷是否有必要執(zhí)行繪制if (mFPSDivisor > 1) { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {// 時(shí)間間隔小于指定的時(shí)間,繼續(xù)請(qǐng)求下一個(gè) vsync 信號(hào)scheduleVsyncLocked();return; }} // 保存當(dāng)前幀的相關(guān)信息mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);mFrameScheduled = false;mLastFrameTimeNanos = frameTimeNanos; } try { // 執(zhí)行相關(guān) callbacks 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); } 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.'); }}
總結(jié)起來(lái)其實(shí)主要是兩個(gè)操作:
4.4.1、設(shè)置當(dāng)前 frame 的啟動(dòng)時(shí)間判斷是否跳幀,若跳幀修正當(dāng)前 frame 的啟動(dòng)時(shí)間到最近的 VSync 信號(hào)時(shí)間。如果沒(méi)跳幀,當(dāng)前 frame 啟動(dòng)時(shí)間直接設(shè)置為當(dāng)前 VSync 信號(hào)時(shí)間。修正完時(shí)間后,無(wú)論當(dāng)前 frame 是否跳幀,使得當(dāng)前 frame 的啟動(dòng)時(shí)間與 VSync 信號(hào)還是在一個(gè)節(jié)奏上的,可能延后了一到幾個(gè)周期,但是都是在下一個(gè) vsync 信號(hào)到來(lái)才進(jìn)行處理 。
如下圖所示是時(shí)間修正的一個(gè)例子,
沒(méi)有跳幀但延遲
由于第二個(gè) frame 執(zhí)行超時(shí),第三個(gè) frame 實(shí)際啟動(dòng)時(shí)間比第三個(gè) VSync 信號(hào)到來(lái)時(shí)間要晚,因?yàn)檫@時(shí)候延時(shí)比較小,沒(méi)有超過(guò)一個(gè)時(shí)鐘周期,系統(tǒng)還是將 frameTimeNanos3 傳給回調(diào),回調(diào)拿到的時(shí)間和 VSync 信號(hào)同步。
再來(lái)看看下圖:
跳幀
由于第二個(gè) frame執(zhí)行時(shí)間超過(guò) 2 個(gè)時(shí)鐘周期,導(dǎo)致第三個(gè) frame 延后執(zhí)行時(shí)間大于一個(gè)時(shí)鐘周期,系統(tǒng)認(rèn)為這時(shí)候影響較大,判定為跳幀了,將第三個(gè) frame 的時(shí)間修正為 frameTimeNanos4,比 VSync 真正到來(lái)的時(shí)間晚了一個(gè)時(shí)鐘周期。
時(shí)間修正,既保證了doFrame操作和 VSync 保持同步節(jié)奏,又保證實(shí)際啟動(dòng)時(shí)間與記錄的時(shí)間點(diǎn)相差不會(huì)太大,便于同步及分析。
4.4.2、順序執(zhí)行callBack隊(duì)列里面的callback然后接下來(lái)看看 doCallbacks 的執(zhí)行過(guò)程:
void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) {// We use 'now' to determine when callbacks become due because it’s possible// for earlier processing phases in a frame to post callbacks that should run// in a following phase, such as an input event that causes an animation to start.final long now = System.nanoTime(); // callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);if (callbacks == null) { return;}mCallbacksRunning = true;// Update the frame time if necessary when committing the frame.// We only update the frame time if we are more than 2 frames late reaching// the commit phase. This ensures that the frame time which is observed by the// callbacks will always increase from one frame to the next and never repeat.// We never want the next frame’s starting frame time to end up being less than// or equal to the previous frame’s commit frame time. Keep in mind that the// next frame has most likely already been scheduled by now so we play it// safe by ensuring the commit time is always at least one frame behind. // commit 類型是最后執(zhí)行的,如果此時(shí)發(fā)現(xiàn)前面處理時(shí)間過(guò)長(zhǎng),就會(huì)進(jìn)行糾正。if (callbackType == Choreographer.CALLBACK_COMMIT) { final long jitterNanos = now - frameTimeNanos; Trace.traceCounter(Trace.TRACE_TAG_VIEW, 'jitterNanos', (int) jitterNanos); if (jitterNanos >= 2 * mFrameIntervalNanos) {final long lastFrameOffset = jitterNanos % mFrameIntervalNanos+ mFrameIntervalNanos;if (DEBUG_JANK) { Log.d(TAG, 'Commit callback delayed by ' + (jitterNanos * 0.000001f) + ' ms which is more than twice the frame interval of ' + (mFrameIntervalNanos * 0.000001f) + ' ms! ' + 'Setting frame time to ' + (lastFrameOffset * 0.000001f) + ' ms in the past.'); mDebugPrintNextFrameTimeDelta = true;}frameTimeNanos = now - lastFrameOffset;mLastFrameTimeNanos = frameTimeNanos; }} } try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);for (CallbackRecord c = callbacks; c != null; c = c.next) { if (DEBUG_FRAMES) {Log.d(TAG, 'RunCallback: type=' + callbackType+ ', action=' + c.action + ', token=' + c.token+ ', latencyMillis=' + (SystemClock.uptimeMillis() - c.dueTime)); }// 運(yùn)行每一個(gè) callback c.run(frameTimeNanos);} } finally {synchronized (mLock) { mCallbacksRunning = false; do {final CallbackRecord next = callbacks.next;recycleCallbackLocked(callbacks);callbacks = next; } while (callbacks != null);}Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}
callback的類型有以下 4 種,除了文章一開(kāi)始提到的 3 種外,還有一個(gè) CALLBACK_COMMIT。
CALLBACK_INPUT:輸入 CALLBACK_ANIMATION:動(dòng)畫 CALLBACK_TRAVERSAL:遍歷,執(zhí)行 measure、layout、draw CALLBACK_COMMIT:遍歷完成的提交操作,用來(lái)修正動(dòng)畫啟動(dòng)時(shí)間然后看上面的源碼,分析一下每個(gè) callback 的執(zhí)行過(guò)程:
1. callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS);
得到執(zhí)行時(shí)間在當(dāng)前時(shí)間之前的所有 CallBack,保存在單鏈表中。每種類型的 callback 按執(zhí)行時(shí)間先后順序排序分別存在一個(gè)單鏈表里面。為了保證當(dāng)前 callback 執(zhí)行時(shí)新 post 進(jìn)來(lái)的 callback 在下一個(gè) frame 時(shí)才被執(zhí)行,這個(gè)地方 extractDueCallbacksLocked 會(huì)將需要執(zhí)行的 callback 和以后執(zhí)行的 callback 斷開(kāi)變成兩個(gè)鏈表,新 post 進(jìn)來(lái)的 callback 會(huì)被放到后面一個(gè)鏈表中。當(dāng)前 frame 只會(huì)執(zhí)行前一個(gè)鏈表中的 callback,保證了在執(zhí)行 callback 時(shí),如果callback中Post相同類型的callback,這些新加的 callback 將在下一個(gè) frame 啟動(dòng)后才會(huì)被執(zhí)行。
2. 接下來(lái),看一大段注釋,如果類型是 CALLBACK_COMMIT,并且當(dāng)前 frame 渲染時(shí)間超過(guò)了兩個(gè)時(shí)鐘周期,則將當(dāng)前提交時(shí)間修正為上一個(gè)垂直同步信號(hào)時(shí)間。為了保證下一個(gè)frame 的提交時(shí)間和當(dāng)前 frame 時(shí)間相差為一且不重復(fù)。這個(gè)地方注釋挺難看懂,實(shí)際上這個(gè)地方 CALLBACK_COMMIT 是為了解決 ValueAnimator 的一個(gè)問(wèn)題而引入的,主要是解決因?yàn)楸闅v時(shí)間過(guò)長(zhǎng)導(dǎo)致動(dòng)畫時(shí)間啟動(dòng)過(guò)長(zhǎng),時(shí)間縮短,導(dǎo)致跳幀,這里修正動(dòng)畫第一個(gè) frame 開(kāi)始時(shí)間延后來(lái)改善,這時(shí)候才表示動(dòng)畫真正啟動(dòng)。為什么不直接設(shè)置當(dāng)前時(shí)間而是回溯一個(gè)時(shí)鐘周期之前的時(shí)間呢?看注釋,這里如果設(shè)置為當(dāng)前 frame 時(shí)間,因?yàn)閯?dòng)畫的第一個(gè) frame 其實(shí)已經(jīng)繪制完成,第二個(gè) frame 這時(shí)候已經(jīng)開(kāi)始了,設(shè)置為當(dāng)前時(shí)間會(huì)導(dǎo)致這兩個(gè) frame 時(shí)間一樣,導(dǎo)致沖突。
如下圖所示:
修正commit時(shí)間
比如說(shuō)在第二個(gè)frame開(kāi)始執(zhí)行時(shí),開(kāi)始渲染動(dòng)畫的第一個(gè)畫面,第二個(gè)frame執(zhí)行時(shí)間超過(guò)了兩個(gè)時(shí)鐘周期,Draw操作執(zhí)行結(jié)束后,這時(shí)候完成了動(dòng)畫第一幀的渲染,動(dòng)畫實(shí)際上還沒(méi)開(kāi)始,但是時(shí)間已經(jīng)過(guò)了兩個(gè)時(shí)鐘周期,后面動(dòng)畫實(shí)際執(zhí)行時(shí)間將會(huì)縮短一個(gè)時(shí)鐘周期。這時(shí)候系統(tǒng)通過(guò)修正commit時(shí)間到frameTimeNanos的上一個(gè)VSync信號(hào)時(shí)間,即完成動(dòng)畫第一幀渲染之前的VSync信號(hào)到來(lái)時(shí)間,修正了動(dòng)畫啟動(dòng)時(shí)間,保證動(dòng)畫執(zhí)行時(shí)間的正確性。
4.4.3、調(diào)用 c.run(frameTimeNanos) 執(zhí)行回調(diào)private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; @UnsupportedAppUsage public void run(long frameTimeNanos) {if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos);} else { ((Runnable)action).run();} }}
CallbackRecord 的代碼如上所示。
4.5、發(fā)起繪制的請(qǐng)求doFrame 的邏輯了解清楚了,但是關(guān)于發(fā)起 vsync 請(qǐng)求的邏輯卻沒(méi)有講。
// ViewRootImpl @UnsupportedAppUsagevoid scheduleTraversals() { // 如果已經(jīng)請(qǐng)求繪制了,就不會(huì)再次請(qǐng)求,因?yàn)槎啻握?qǐng)求,只有有一個(gè)執(zhí)行就滿足要求了 if (!mTraversalScheduled) {mTraversalScheduled = true;// 同步mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// 發(fā)送一個(gè) callback 用于在下一幀來(lái)臨時(shí)候處理mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput();} // 通知稍候繪制notifyRendererOfFramePending();pokeDrawLockIfNeeded(); }}
接著會(huì)調(diào)用postCallbackDelayed:
public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) { if (action == null) {throw new IllegalArgumentException('action must not be null'); } if (callbackType < 0 || callbackType > CALLBACK_LAST) {throw new IllegalArgumentException('callbackType is invalid'); } postCallbackDelayedInternal(callbackType, action, token, delayMillis);}
主要是做一些邏輯判斷,確保傳入的是對(duì)的。
接著又會(huì)調(diào)用postCallbackDelayedInternal,保存 callback,并發(fā)起請(qǐng)求下一幀。
private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) {Log.d(TAG, 'PostCallback: type=' + callbackType+ ', action=' + action + ', token=' + token+ ', delayMillis=' + delayMillis); } synchronized (mLock) {final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);// 小于當(dāng)前時(shí)間,說(shuō)明需要立即執(zhí)行if (dueTime <= now) { scheduleFrameLocked(now);} else { // 發(fā)送一個(gè)延遲 msg Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime);} }}
簡(jiǎn)單來(lái)說(shuō),就是判斷當(dāng)前是否有必要發(fā)起一個(gè)繪制請(qǐng)求,比如你發(fā)了一個(gè) 500ms 后重繪的消息,對(duì)于這個(gè)消息,會(huì)在 500ms 后在進(jìn)行處理。但如果不是延遲消息,那說(shuō)明需要立即處理。但是對(duì)于 view 的繪制邏輯,必須得等到下一個(gè) vsync 到來(lái)的時(shí)候才會(huì)真正進(jìn)行繪制。
接下來(lái)看看scheduleFrameLocked 的邏輯:
private void scheduleFrameLocked(long now) {if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) {if (DEBUG_FRAMES) { Log.d(TAG, 'Scheduling next frame on vsync.');}// If running on the Looper thread, then schedule the vsync immediately,// otherwise post a message to schedule the vsync from the UI thread// as soon as possible. // 如果是在一個(gè) looper 線程中,那么直接執(zhí)行請(qǐng)求就好if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked();} else { // 如果是在主線程,那么需要發(fā)送一個(gè)請(qǐng)求 vsync 的消息,并插到最前面,需要確保前一個(gè)消息處理完后在開(kāi)始請(qǐng)求 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg);} } else {// 如果不用 vsync 信號(hào),那么就可以直接執(zhí)行,只是需要記錄每一幀的時(shí)間final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) { Log.d(TAG, 'Scheduling next frame in ' + (nextFrameTime - now) + ' ms.');}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime); }} }
這里主要是根據(jù)具體情況來(lái)判斷如何發(fā)起下一幀的繪制。對(duì)于采用 vsync 信號(hào)的主線程,會(huì)發(fā)送一個(gè)MSG_DO_SCHEDULE_VSYNC 的消息,插到最前面,確保可以最早執(zhí)行。
當(dāng)收到 MSG_DO_SCHEDULE_VSYNC 消息后,就會(huì)給他安排請(qǐng)求 vsync 信號(hào)的請(qǐng)求,最后會(huì)會(huì)調(diào)到 onVsync 方法。
void doScheduleVsync() { synchronized (mLock) {if (mFrameScheduled) { scheduleVsyncLocked();} }}@UnsupportedAppUsageprivate void scheduleVsyncLocked() { // 發(fā)起獲取 vsync 信號(hào)的請(qǐng)求 mDisplayEventReceiver.scheduleVsync();} /** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */@UnsupportedAppUsagepublic 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); }}
到這里,就把Choreographer 的基本原理都講完了。
五、源碼小結(jié)Choreographer是線程單例的,而且必須要和一個(gè) Looper 綁定,因?yàn)槠鋬?nèi)部有一個(gè) Handler 需要和 Looper 綁定,一般是 App 主線程的 Looper 綁定。
DisplayEventReceiver是一個(gè) abstract class,其 JNI 的代碼部分會(huì)創(chuàng)建一個(gè)IDisplayEventConnection 的 Vsync 監(jiān)聽(tīng)者對(duì)象。這樣,來(lái)自 AppEventThread 的 VSYNC 中斷信號(hào)就可以傳遞給 Choreographer 對(duì)象了。當(dāng) Vsync 信號(hào)到來(lái)時(shí),DisplayEventReceiver 的 onVsync 函數(shù)將被調(diào)用。
DisplayEventReceiver還有一個(gè) scheduleVsync 函數(shù)。當(dāng)應(yīng)用需要繪制UI時(shí),將首先申請(qǐng)一次 Vsync 中斷,然后再在中斷處理的 onVsync 函數(shù)去進(jìn)行繪制。
Choreographer定義了一個(gè)FrameCallbackinterface,每當(dāng) Vsync 到來(lái)時(shí),其 doFrame 函數(shù)將被調(diào)用。這個(gè)接口對(duì) Android Animation 的實(shí)現(xiàn)起了很大的幫助作用。以前都是自己控制時(shí)間,現(xiàn)在終于有了固定的時(shí)間中斷。
Choreographer的主要功能是,當(dāng)收到 Vsync 信號(hào)時(shí),去調(diào)用使用者通過(guò) postCallback 設(shè)置的回調(diào)函數(shù)。目前一共定義了五種類型的回調(diào),它們分別是:ListView的 Item 初始化(obtainsetup) 會(huì)在 input 里面也會(huì)在 animation 里面,這取決于
CALLBACK_INPUT: 處理輸入事件處理有關(guān) CALLBACK_ANIMATION: 處理 Animation 的處理有關(guān) CALLBACK_INSETS_ANIMATION: 處理 Insets Animation 的相關(guān)回調(diào) CALLBACK_TRAVERSAL: 處理和 UI 等控件繪制有關(guān) CALLBACK_COMMIT: 處理 Commit 相關(guān)回調(diào)CALLBACK_INPUT、CALLBACK_ANIMATION會(huì)修改 view 的屬性,所以要比 CALLBACK_TRAVERSAL 先執(zhí)行
最后附上一張時(shí)序圖,來(lái)回顧一下整個(gè)流程
以上就是分析Android Choreographer源碼的詳細(xì)內(nèi)容,更多關(guān)于Android Choreographer的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. asp在iis7報(bào)錯(cuò)行號(hào)不準(zhǔn)問(wèn)題的解決方法2. 三個(gè)不常見(jiàn)的 HTML5 實(shí)用新特性簡(jiǎn)介3. ASP中解決“對(duì)象關(guān)閉時(shí),不允許操作。”的詭異問(wèn)題……4. 原生js XMLhttprequest請(qǐng)求onreadystatechange執(zhí)行兩次的解決5. 刪除docker里建立容器的操作方法6. CSS代碼檢查工具stylelint的使用方法詳解7. msxml3.dll 錯(cuò)誤 800c0019 系統(tǒng)錯(cuò)誤:-2146697191解決方法8. jsp實(shí)現(xiàn)簡(jiǎn)單用戶7天內(nèi)免登錄9. CSS3實(shí)現(xiàn)動(dòng)態(tài)翻牌效果 仿百度貼吧3D翻牌一次動(dòng)畫特效10. 匹配模式 - XSL教程 - 4
