Android圖形系統(tǒng)(十一)-Choreographer

在Android4.1之后增加了Choreographer機(jī)制,用于同Vsync機(jī)制配合,統(tǒng)一動(dòng)畫、輸入和繪制時(shí)機(jī)。本文以繪制為例來簡(jiǎn)單學(xué)習(xí)下Choreographer。

一、從繪制流程開始

ViewRootImpl的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);
        }
    }

這里主要關(guān)注兩點(diǎn):

postSyncBarrier : Handler 的同步屏障。它的作用是可以攔截 Looper 對(duì)同步消息的獲取和分發(fā),加入同步屏障之后,Looper 只會(huì)獲取和處理異步消息,如果沒有異步消息那么就會(huì)進(jìn)入阻塞狀態(tài)。也就是說,對(duì)View繪制渲染的處理操作可以優(yōu)先處理(設(shè)置為異步消息)。

Choreographer: 編舞者。統(tǒng)一動(dòng)畫、輸入和繪制時(shí)機(jī)。也是這章需要重點(diǎn)分析的內(nèi)容。

二、Choreographer啟動(dòng)
public ViewRootImpl(Context context, Display display) {
    ...
    //獲取Choreographer實(shí)例
    mChoreographer = Choreographer.getInstance();
    ...
}

frameworks\base\core\java\android\view\Choreographer.java

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è)Looper線程都有自己的Choreographer,其他線程發(fā)送的回調(diào)只能運(yùn)行在對(duì)應(yīng)Choreographer所屬的Looper線程上

private Choreographer(Looper looper) {
   mLooper = looper;
   mHandler = new FrameHandler(looper);
   // 根據(jù)是否使用了VSYNC來創(chuàng)建一個(gè)FrameDisplayEventReceiver對(duì)象
   mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
   mLastFrameTimeNanos = Long.MIN_VALUE;//是指上一次幀繪制時(shí)間點(diǎn)
   mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());//幀間時(shí)長(zhǎng),一般等于16.7ms
   // CALLBACK_LAST + 1 = 4,創(chuàng)建一個(gè)容量為4的CallbackQueue數(shù)組,用來存放4種不同的Callback
   mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
   for (int i = 0; i <= CALLBACK_LAST; i++) {
       mCallbackQueues[i] = new CallbackQueue();
   }
}

Choreographer類中有一個(gè)Looper和一個(gè)FrameHandler變量。變量USE_VSYNC用于表示系統(tǒng)是否是用了Vsync同步機(jī)制,該值是通過讀取系統(tǒng)屬性debug.choreographer.vsync來獲取的。如果系統(tǒng)使用了Vsync同步機(jī)制,則創(chuàng)建一個(gè)FrameDisplayEventReceiver對(duì)象用于請(qǐng)求并接收Vsync事件,最后Choreographer創(chuàng)建了一個(gè)大小為3的CallbackQueue隊(duì)列數(shù)組,用于保存不同類型的Callback。

這里,不同類型的Callback包括如下4種:

public static final int CALLBACK_INPUT = 0; //輸入
public static final int CALLBACK_ANIMATION = 1; //動(dòng)畫
public static final int CALLBACK_TRAVERSAL = 2; //視圖繪制
public static final int CALLBACK_COMMIT = 3; //提交 ( 這一類型是在API level=23的時(shí)候添加的)

CallbackQueue是一個(gè)容量為4的數(shù)組,每一個(gè)元素作為頭指針,引出對(duì)應(yīng)類型的鏈表,4種事件就是通過這4個(gè)鏈表來維護(hù)的。

而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);
               break;
           case MSG_DO_SCHEDULE_VSYNC:
               doScheduleVsync();   // 請(qǐng)求VSYNC信號(hào)
               break;
           case MSG_DO_SCHEDULE_CALLBACK:
               doScheduleCallback(msg.arg1);
               break;
       }
   }
}
三、Choreographer執(zhí)行流程
i

Choreographer提供了兩類添加回調(diào)的方式:postCallback 與 postFrameCallback,當(dāng)然對(duì)應(yīng)類型也包含delay的方法,算上其實(shí)有4個(gè)方法。

postCallback對(duì)應(yīng)的:

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);
}

postFrameCallback對(duì)應(yīng)的:

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
    if (callback == null) {
        throw new IllegalArgumentException("callback must not be null");
    }
    postCallbackDelayedInternal(CALLBACK_ANIMATION,
            callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

相比之下postCallback更靈活一點(diǎn)。兩者最終都會(huì)調(diào)到: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)看源碼,非主要部分,此處略過),然后CallbackQueue根據(jù)回調(diào)類型將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類型的定時(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添加到鏈表上來,然后判斷是否有延遲,如果沒有則會(huì)馬上執(zhí)行scheduleFrameLocked,如果有,則發(fā)送一個(gè)what為MSG_DO_SCHEDULE_CALLBACK類型的定時(shí)消息,等時(shí)間到了再處理,其最終處理也是執(zhí)行scheduleFrameLocked(long now)方法。

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 {
           // 沒有使用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);
       }
   }
}

這里首先判斷USE_VSYNC,如果使用了VSYNC:走scheduleVsyncLocked,即請(qǐng)求VSYNC信號(hào),最終調(diào)用doFrame,如果沒使用VSYNC,則通過消息執(zhí)行doFrame。

那么我們先簡(jiǎn)單了解下請(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 , 主要是用來接收同步脈沖信號(hào) VSYNC。scheduleVsync()方法通過底層nativeScheduleVsync()向SurfaceFlinger 服務(wù)注冊(cè),即在下一次脈沖接收后會(huì)調(diào)用 DisplayEventReceiver的dispatchVsync()方法。這里類似于訂閱者模式,但是每次調(diào)用nativeScheduleVsync()方法都有且只有一次dispatchVsync()方法回調(diào)。

然后再看看接收VSYNC信號(hào):

底層向應(yīng)用層發(fā)送VSYNC信號(hào),java層通過dispatchVsync()接收,最后回調(diào)在FrameDisplayEventReceiver的onVsync

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) {
        //忽略來自第二顯示屏的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()過程是通過FrameHandler向主線程Looper發(fā)送了一個(gè)自帶callback的消息 callback為FrameDisplayEventReceiver。 當(dāng)主線程Looper執(zhí)行到該消息時(shí),則調(diào)用FrameDisplayEventReceiver.run()方法,緊接著便是調(diào)用doFrame。

void doFrame(long frameTimeNanos, int frame) {
    final long startNanos;
    synchronized (mLock) {
        if (!mFrameScheduled) {
            return; // mFrameScheduled=false,則直接返回。
        }
        long intendedFrameTimeNanos = frameTimeNanos; //原本計(jì)劃的繪幀時(shí)間點(diǎn)
        startNanos = System.nanoTime();//保存起始時(shí)間
        //由于Vsync事件處理采用的是異步方式,因此這里計(jì)算消息發(fā)送與函數(shù)調(diào)用開始之間所花費(fèi)的時(shí)間
        final long jitterNanos = startNanos - frameTimeNanos;
        //如果線程處理該消息的時(shí)間超過了屏幕刷新周期
        if (jitterNanos >= mFrameIntervalNanos) {
            //計(jì)算函數(shù)調(diào)用期間所錯(cuò)過的幀數(shù)
            final long skippedFrames = jitterNanos / mFrameIntervalNanos;
            //當(dāng)?shù)魩瑐€(gè)數(shù)超過30,則輸出相應(yīng)log
            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;
            frameTimeNanos = startNanos - lastFrameOffset; //對(duì)齊幀的時(shí)間間隔
        }
       //如果frameTimeNanos小于一個(gè)屏幕刷新周期,則重新請(qǐng)求VSync信號(hào)
        if (frameTimeNanos < mLastFrameTimeNanos) {
            scheduleVsyncLocked();
            return;
        }
        mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
        mFrameScheduled = false;
        mLastFrameTimeNanos = frameTimeNanos;
    }
    try {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
        //分別回調(diào)CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL事件
        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);
    }
}

當(dāng)Vsync事件到來時(shí),順序執(zhí)行4種事件對(duì)應(yīng)CallbackQueue隊(duì)列中注冊(cè)的回調(diào)。

void doCallbacks(int callbackType, long frameTimeNanos) {
    CallbackRecord callbacks;
    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        //從指定類型的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);
        }
    }
}

按時(shí)間順序先后執(zhí)行CallbackRecord對(duì)應(yīng)的run方法

private static final class CallbackRecord {
    public CallbackRecord next;
    public long dueTime;
    public Object action; // Runnable or FrameCallback
    public Object token;
    public void run(long frameTimeNanos) {
        if (token == FRAME_CALLBACK_TOKEN) {
            ((FrameCallback)action).doFrame(frameTimeNanos);
        } else {
            ((Runnable)action).run();
        }
    }
}

接開篇講的

 void scheduleTraversals() {
     ...
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }

mTraversalRunnable對(duì)應(yīng):

final class TraversalRunnable implements Runnable {
   @Override
   public void run() {
       doTraversal();
   }
}

run方法被執(zhí)行,所以doTraversal()被執(zhí)行,開啟View的繪制流程。

所以整個(gè)繪制過程總的流程如下所示:

簡(jiǎn)單總結(jié):

  • Choreographer支持4種類型事件:輸入、繪制、動(dòng)畫、提交,并通過postCallback在對(duì)應(yīng)需要同步vsync進(jìn)行刷新處進(jìn)行注冊(cè),等待回調(diào)。
  • Choreographer監(jiān)聽底層Vsync信號(hào),一旦接收到回調(diào)信號(hào),則通過doFrame統(tǒng)一對(duì)java層4種類型事件進(jìn)行回調(diào)。

參考
https://blog.csdn.net/bluewindtalker/article/details/54017569
https://blog.csdn.net/qian520ao/article/details/80954626
http://gityuan.com/2017/02/25/choreographer/
http://m.itdecent.cn/p/47c866f6fb67

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載于:請(qǐng)叫我大蘇的 Android屏幕刷新機(jī)制 我主要的目的是跟著文章的思路從新走一遍,讓自己更好的理解相關(guān)的知...
    ghroost閱讀 2,590評(píng)論 2 11
  • 這篇博文是參考別人的博客,結(jié)合源碼自己又走了一遍,僅供自己記錄學(xué)習(xí)。 我們要掌握android,那么關(guān)于andro...
    scarecrowtb閱讀 2,265評(píng)論 1 3
  • 為什么叫舞蹈編導(dǎo),因?yàn)槲璧甘怯晒?jié)奏的,節(jié)奏是每個(gè)點(diǎn)位動(dòng)作的快慢控制,跳舞時(shí)節(jié)奏很重要,編舞者控制節(jié)奏。視圖刷新也是...
    gczxbb閱讀 4,425評(píng)論 1 9
  • Android系統(tǒng)從4.1(API 16)開始加入Choreographer這個(gè)類來控制同步處理輸入(Input)...
    DeltaTech閱讀 35,997評(píng)論 22 139
  • 古人云:凡謀之道,周密為寶。又云:預(yù)則立,不預(yù)則廢。從小到大的教育總是提醒我們?nèi)f事要先做好規(guī)劃,掌控局面才能...
    七上小下閱讀 245評(píng)論 0 0

友情鏈接更多精彩內(nèi)容