屏幕刷新機(jī)制 Choreographer 原理分析
本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,作者Android開(kāi)發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。
前言
Android平臺(tái)提供兩種信號(hào),一種是硬件信號(hào),另一種是軟件信號(hào),由SurfaceFlinger進(jìn)程的一個(gè)線程定時(shí)發(fā)出,硬件信號(hào)由硬件發(fā)出;
App進(jìn)程若要通過(guò)gpu實(shí)現(xiàn)圖像繪制,需要在接收到Vsync信號(hào)的條件下進(jìn)行,因此,App進(jìn)程訪問(wèn)SurfaceFlinger進(jìn)程獲取這個(gè)信號(hào),再進(jìn)行g(shù)pu繪制;
Android4.1之后增加了Choreographer機(jī)制,用于同Vsync機(jī)制配合,統(tǒng)一動(dòng)畫(huà)、輸入和繪制時(shí)機(jī);
Choreographer就是負(fù)責(zé)獲取Vsync同步信號(hào)并控制App線程(主線程)完成圖像繪制的類(lèi);
今天我們就來(lái)聊聊Choreographer機(jī)制;
一、Choreographer類(lèi)介紹
1、實(shí)例初始化
- public ViewRootImpl(Context context, Display display) {
- ...
- //獲取Choreographer實(shí)例
- mChoreographer = Choreographer.getInstance();
- ...
- }
- public static Choreographer getInstance() {
- return sThreadInstance.get();
- }
- 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!");
- }
- return new Choreographer(looper);
- }
- };
- 每個(gè)線程中保存一個(gè)Choreographer實(shí)例對(duì)象;
- 線程本地存儲(chǔ)ThreadLocal變量,Choreographer類(lèi)型,在主線程中初始化變量時(shí),創(chuàng)建Choreographer對(duì)象,綁定主線程Looper;
- 同一個(gè)App的每個(gè)窗體旗下ViewRootImpl使用的同一個(gè)Choregrapher對(duì)象,他控制者整個(gè)App中大部分視圖的繪制節(jié)奏。
2、構(gòu)造方法
- private Choreographer(Looper looper, int vsyncSource) {
- mLooper = looper;
- //使用當(dāng)前線程looper創(chuàng)建 mHandler
- mHandler = new FrameHandler(looper);
- //USE_VSYNC 4.1以上默認(rèn)是true,表示 具備接受VSync的能力,這個(gè)接受能力就是FrameDisplayEventReceiver
- mDisplayEventReceiver = USE_VSYNC
- ? new FrameDisplayEventReceiver(looper, vsyncSource)
- : null;
- mLastFrameTimeNanos = Long.MIN_VALUE;
- // 計(jì)算一幀的時(shí)間,Android手機(jī)屏幕是60Hz的刷新頻率,就是16ms
- mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
- // 創(chuàng)建一個(gè)鏈表類(lèi)型CallbackQueue的數(shù)組,大小為5,
- //也就是數(shù)組中有五個(gè)鏈表,每個(gè)鏈表存相同類(lèi)型的任務(wù):輸入、動(dòng)畫(huà)、遍歷繪制等任務(wù)(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
- mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
- 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è)Looper和一個(gè)FrameHandler變量;
- 變量USE_VSYNC用于表示系統(tǒng)是否是用了Vsync同步機(jī)制,該值是通過(guò)讀取系統(tǒng)屬性debug.choreographer.vsync來(lái)獲取的;
- 系統(tǒng)使用了Vsync同步機(jī)制,創(chuàng)建一個(gè)FrameDisplayEventReceiver對(duì)象用于請(qǐng)求并接收Vsync事件;
- Choreographer創(chuàng)建了一個(gè)大小為3的CallbackQueue隊(duì)列數(shù)組,用于保存不同類(lèi)型的Callback。
3、Callback類(lèi)型
- //輸入事件,首先執(zhí)行
- public static final int CALLBACK_INPUT = 0;
- //動(dòng)畫(huà),第二執(zhí)行
- public static final int CALLBACK_ANIMATION = 1;
- //插入更新的動(dòng)畫(huà),第三執(zhí)行
- public static final int CALLBACK_INSETS_ANIMATION = 2;
- //繪制,第四執(zhí)行
- public static final int CALLBACK_TRAVERSAL = 3;
- //提交,最后執(zhí)行,
- public static final int CALLBACK_COMMIT = 4;
五種類(lèi)型任務(wù)對(duì)應(yīng)存入對(duì)應(yīng)的CallbackQueue中;
每當(dāng)收到 VSYNC 信號(hào)時(shí),Choreographer 將首先處理 INPUT 類(lèi)型的任務(wù),然后是 ANIMATION 類(lèi)型,最后才是 TRAVERSAL 類(lèi)型。
4、FrameHandler處理的消息
- CallbackQueue是一個(gè)容量為4的數(shù)組,每一個(gè)元素作為頭指針,引出對(duì)應(yīng)類(lèi)型的鏈表,4種事件就是通過(guò)這4個(gè)鏈表來(lái)維護(hù)的;
- 而FrameHandler中主要處理三類(lèi)消息:
- 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(); // 請(qǐng)求VSYNC信號(hào)
- break;
- case MSG_DO_SCHEDULE_CALLBACK:
- doScheduleCallback(msg.arg1);
- break;
- }
- }
- }
- MSG_DO_FRAME 處理注冊(cè)在Choreographer 的Runnable;
- MSG_DO_SCHEDULE_VSYNC 直接請(qǐng)求下一幀的VSync信號(hào);
- MSG_DO_SCHEDULE_CALLBACK 根據(jù)Choreographer的配置執(zhí)行合適的Handler延時(shí)處理;
二、Choreographer執(zhí)行流程
1、requestLayout
- @Override
- public void requestLayout() {
- if (!mHandlingLayoutInLayoutRequest) {
- checkThread();//檢查是否在當(dāng)前線程
- mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
- scheduleTraversals();
- }
- }
- void scheduleTraversals() {
- if (!mTraversalScheduled) {//同一幀內(nèi)不會(huì)多次調(diào)用遍歷
- mTraversalScheduled = true;
- mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//攔截同步Message
- //Choreographer回調(diào),執(zhí)行繪制操作
- mChoreographer.postCallback(
- Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- }
- }
- postSyncBarrier : Handler 的同步屏障,它的作用是可以攔截 Looper 對(duì)同步消息的獲取和分發(fā),加入同步屏障之后,Looper 只會(huì)獲取和處理異步消息,如果沒(méi)有異步消息那么就會(huì)進(jìn)入阻塞狀態(tài);
- Choreographer: 編舞者,統(tǒng)一動(dòng)畫(huà)、輸入和繪制時(shí)機(jī);
- mChoreographer.postCallback(
- Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- postCallback()->postCallbackDelayed()->postCallbackDelayedInternal():
2、postCallbackDelayedInternal
- private void postCallbackDelayedInternal(int callbackType,
- Object action, Object token, long delayMillis) {
- synchronized (mLock) {
- // 當(dāng)前時(shí)間
- final long now = SystemClock.uptimeMillis();
- // 回調(diào)執(zhí)行時(shí)間,為當(dāng)前時(shí)間加上延遲的時(shí)間
- final long dueTime = now + delayMillis;
- // obtainCallbackLocked(long dueTime, Object action, Object token)會(huì)將傳入的3個(gè)參數(shù)轉(zhuǎn)換為CallbackRecord(具體請(qǐng)看源碼,非主要部分,此處略過(guò)),然后CallbackQueue根據(jù)回調(diào)類(lèi)型將CallbackRecord添加到鏈表上。
- mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
- if (dueTime <= now) {
- // 如果delayMillis=0的話,dueTime=now,則會(huì)馬上執(zhí)行
- scheduleFrameLocked(now);
- } else {
- // 如果dueTime>now,則發(fā)送一個(gè)what為MSG_DO_SCHEDULE_CALLBACK類(lèi)型的定時(shí)消息,等時(shí)間到了再處理,其最終處理也是執(zhí)行scheduleFrameLocked(long now)方法
- Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
- msg.arg1 = callbackType;
- msg.setAsynchronous(true);
- mHandler.sendMessageAtTime(msg, dueTime);
- }
- }
- }
- mCallbackQueues先把對(duì)應(yīng)的callback添加到鏈表上來(lái),然后判斷是否有延遲;
- 如果沒(méi)有則會(huì)馬上執(zhí)行scheduleFrameLocked,如果有,則發(fā)送一個(gè)what為MSG_DO_SCHEDULE_CALLBACK類(lèi)型的定時(shí)消息,等時(shí)間到了再處理;
- 其最終處理也是執(zhí)行scheduleFrameLocked(long now)方法;
3、scheduleFrameLocked
- private void scheduleFrameLocked(long now) {
- if (!mFrameScheduled) {
- mFrameScheduled = true;
- if (USE_VSYNC) {
- // 如果使用了VSYNC,由系統(tǒng)值確定
- if (DEBUG_FRAMES) {
- Log.d(TAG, "Scheduling next frame on vsync.");
- }
- if (isRunningOnLooperThreadLocked()) {
- // 請(qǐng)求VSYNC信號(hào),最終會(huì)調(diào)到Native層,Native處理完成后觸發(fā)FrameDisplayEventReceiver的onVsync回調(diào),回調(diào)中最后也會(huì)調(diào)用doFrame(long frameTimeNanos, int frame)方法
- scheduleVsyncLocked();
- } else {
- // 在UI線程上直接發(fā)送一個(gè)what=MSG_DO_SCHEDULE_VSYNC的消息,最終也會(huì)調(diào)到scheduleVsyncLocked()去請(qǐng)求VSYNC信號(hào)
- Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
- msg.setAsynchronous(true);
- mHandler.sendMessageAtFrontOfQueue(msg);
- }
- } else {
- // 沒(méi)有使用VSYNC
- 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.");
- }
- // 直接發(fā)送一個(gè)what=MSG_DO_FRAME的消息,消息處理時(shí)調(diào)用doFrame(long frameTimeNanos, int frame)方法
- Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
- msg.setAsynchronous(true);
- mHandler.sendMessageAtTime(msg, nextFrameTime);
- }
- }
- }
- // Enable/disable vsync for animations and drawing.
- private static final boolean USE_VSYNC = SystemProperties.getBoolean(
- "debug.choreographer.vsync", true);
常量USE_VSYNC,表示是否允許動(dòng)畫(huà)和繪制的垂直同步,默認(rèn)是為true;
判斷USE_VSYNC,如果使用了VSYNC:走scheduleVsyncLocked,即請(qǐng)求VSYNC信號(hào),最終調(diào)用doFrame;
如果沒(méi)使用VSYNC,則通過(guò)消息執(zhí)行doFrame;
4、scheduleVsyncLocked
請(qǐng)求VSYNC信號(hào)的流程;
- private void scheduleVsyncLocked() {
- mDisplayEventReceiver.scheduleVsync();
- }
- 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);
- }
- }
- mDisplayEventReceiver 對(duì)應(yīng)的是FrameDisplayEventReceiver,它繼承自 DisplayEventReceiver , 主要是用來(lái)接收同步脈沖信號(hào) VSYNC;
- scheduleVsync()方法通過(guò)底層nativeScheduleVsync()向SurfaceFlinger 服務(wù)注冊(cè),即在下一次脈沖接收后會(huì)調(diào)用 DisplayEventReceiver的dispatchVsync()方法;
- 這里類(lèi)似于訂閱者模式,但是每次調(diào)用nativeScheduleVsync()方法都有且只有一次dispatchVsync()方法回調(diào);
- 底層向應(yīng)用層發(fā)送VSYNC信號(hào),java層通過(guò)dispatchVsync()接收,最后回調(diào)在FrameDisplayEventReceiver的onVsync;
5、FrameDisplayEventReceiver
- private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
- private boolean mHavePendingVsync;
- private long mTimestampNanos;
- private int mFrame;
- @Override
- public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
- //忽略來(lái)自第二顯示屏的Vsync
- if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
- scheduleVsync();
- return;
- }
- ...
- mTimestampNanos = timestampNanos;
- mFrame = frame;
- //該消息的callback為當(dāng)前對(duì)象FrameDisplayEventReceiver
- Message msg = Message.obtain(mHandler, this);
- msg.setAsynchronous(true);
- //此處mHandler為FrameHandler
- mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
- }
- @Override
- public void run() {
- mHavePendingVsync = false;
- doFrame(mTimestampNanos, mFrame);
- }
- }
- onVsync()過(guò)程是通過(guò)FrameHandler向主線程Looper發(fā)送了一個(gè)自帶callback的消息 callback為FrameDisplayEventReceiver;
- 當(dāng)主線程Looper執(zhí)行到該消息時(shí),則調(diào)用FrameDisplayEventReceiver.run()方法,緊接著便是調(diào)用doFrame;
6、doFrame
- void doFrame(long frameTimeNanos, int frame) {
- final long startNanos;
- synchronized (mLock) {
- ...
- //是否有跳幀,如果有那么就打印log并且修正偏差
- }
- //執(zhí)行callback
- 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);
- 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.");
- }
- }
doFrame方法做的就是渲染下一幀,檢測(cè)是否卡頓并修補(bǔ)卡頓,然后開(kāi)始做渲染工作,doCallbacks方法的參數(shù):
- CALLBACK_INPUT:輸入;
- CALLBACK_ANIMATION:動(dòng)畫(huà);
- CALLBACK_TRAVERSAL:遍歷,執(zhí)行measure、layout、draw;
- CALLBACK_COMMIT:遍歷完成的提交操作,用來(lái)修正動(dòng)畫(huà)啟動(dòng)時(shí)間;
7、doCallbacks
- void doCallbacks(int callbackType, long frameTimeNanos) {
- CallbackRecord callbacks;
- synchronized (mLock) {
- final long now = SystemClock.uptimeMillis();
- //從指定類(lèi)型的CallbackQueue隊(duì)列中查找執(zhí)行時(shí)間到的CallbackRecord
- callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
- if (callbacks == null) {
- return;
- }
- mCallbacksRunning = true;
- }
- try {
- //由于CallbackQueues是按時(shí)間先后順序排序的,因此遍歷執(zhí)行所有時(shí)間到的CallbackRecord
- for (CallbackRecord c = callbacks; c != null; c = c.next) {
- c.run(frameTimeNanos);
- }
- } finally {
- synchronized (mLock) {
- mCallbacksRunning = false;
- do {
- final CallbackRecord next = callbacks.next;
- recycleCallbackLocked(callbacks);
- callbacks = next;
- } while (callbacks != null);
- }
- }
- }
Choreographer內(nèi)部維護(hù)了這四種鏈表,渲染每一幀的時(shí)候都會(huì)從上往下的去執(zhí)行相應(yīng)的渲染操作,有輸入那么就先渲染輸入隊(duì)列,有動(dòng)畫(huà)就渲染動(dòng)畫(huà),然后遍歷,然后提交;
8、Choreographer總結(jié)
- 控制外部輸入事件處理,動(dòng)畫(huà)執(zhí)行,UI變化,以及提交執(zhí)行都是在同一個(gè)類(lèi)中做的處理,即是Choreographer;
- Choreographer支持4種類(lèi)型事件:輸入、繪制、動(dòng)畫(huà)、提交,并通過(guò)postCallback在對(duì)應(yīng)需要同步vsync進(jìn)行刷新處進(jìn)行注冊(cè),等待回調(diào);
- 每次執(zhí)行的時(shí)候,Choreographer會(huì)根據(jù)當(dāng)前的時(shí)間,只處理事件鏈表中最后一個(gè)事件,當(dāng)有耗時(shí)操作在主線程時(shí),事件不能及時(shí)執(zhí)行,就會(huì)出現(xiàn)所謂的“跳幀”,“卡頓”現(xiàn)象;
- Choreographer的共有方法postCallback(callbackType, Object)是往事件鏈表中放事件的方法,而doFrame()是消耗這些事件的方法;
- Choreographer監(jiān)聽(tīng)底層Vsync信號(hào),一旦接收到回調(diào)信號(hào),則通過(guò)doFrame統(tǒng)一對(duì)java層4種類(lèi)型事件進(jìn)行回調(diào)。