Choreographer主要作用是協(xié)調(diào)輸入酥泛,動畫嘉裤,繪制等任務(wù)的執(zhí)行時機犹芹,它從顯示子系統(tǒng)定時脈沖(垂直同步),然后安排下一個顯示frame的部分工作阁苞。
背景
每次WindowManagerImpl類addView的時候困檩,會創(chuàng)建一個ViewRootImpl的實例,然后調(diào)用setView方法那槽,開啟繪制的mesure悼沿,layout,draw流程
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
public ViewRootImpl(Context context, Display display) {
mChoreographer = Choreographer.getInstance();
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
添加同步屏障骚灸,保證 VSync 到來立即執(zhí)行繪制
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
交給Choreographer調(diào)度
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
后面開始mesure糟趾,layout,draw
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
}
Choreographer類
/**
* Coordinates the timing of animations, input and drawing.
* <p>
* The choreographer receives timing pulses (such as vertical synchronization)
* from the display subsystem then schedules work to occur as part of rendering
* the next display frame.
* </p><p>
* Applications typically interact with the choreographer indirectly using
* higher level abstractions in the animation framework or the view hierarchy.
* Here are some examples of things you can do using the higher-level APIs.
* </p>
* <ul>
* <li>To post an animation to be processed on a regular time basis synchronized with
* display frame rendering, use {@link android.animation.ValueAnimator#start}.</li>
* <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
* frame, use {@link View#postOnAnimation}.</li>
* <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
* frame after a delay, use {@link View#postOnAnimationDelayed}.</li>
* <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the
* next display frame, use {@link View#postInvalidateOnAnimation()} or
* {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li>
* <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in
* sync with display frame rendering, do nothing. This already happens automatically.
* {@link View#onDraw} will be called at the appropriate time.</li>
* </ul>
* <p>
* However, there are a few cases where you might want to use the functions of the
* choreographer directly in your application. Here are some examples.
* </p>
* <ul>
* <li>If your application does its rendering in a different thread, possibly using GL,
* or does not use the animation framework or view hierarchy at all
* and you want to ensure that it is appropriately synchronized with the display, then use
* {@link Choreographer#postFrameCallback}.</li>
* <li>... and that's about it.</li>
* </ul>
* <p>
* Each {@link Looper} thread has its own choreographer. Other threads can
* post callbacks to run on the choreographer but they will run on the {@link Looper}
* to which the choreographer belongs.
* </p>
*/
- 協(xié)調(diào)動畫甚牲,輸入义郑,繪制等優(yōu)先級
- 如果你的程序在獨立的線程中繪制,也可以使用它
- 每個線程有獨立的choreographer
vsync繪制信號
1鳖藕、用ThreadLocal存儲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!");
}
return new Choreographer(looper, VSYNC_SOURCE_APP);
}
};
SurfaceView使用
private static final ThreadLocal<Choreographer> sSfThreadInstance =
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, VSYNC_SOURCE_SURFACE_FLINGER);
public static Choreographer getInstance() {
return sThreadInstance.get();
}
1、構(gòu)造方法著恩,F(xiàn)rameDisplayEventReceiver用來接收VSYNC信號
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
2院尔、在FrameDisplayEventReceiver的構(gòu)造方法完成nativeInit()注冊,然后如果有Vsync信號過來喉誊,會回調(diào)onVsync()方法邀摆,然后handler發(fā)消息,調(diào)用doFrame();
public abstract class DisplayEventReceiver {
public DisplayEventReceiver(Looper looper, int vsyncSource) {
....
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource);
....
}
}
private 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);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, 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);
}
}
3伍茄、回調(diào)doCallbacks()方法
void doFrame(long frameTimeNanos, int frame) {
每兩幀的時間差
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
Log.d(TAG, "Frame time delta: "
+ ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
}
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
計算掉幀數(shù)
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;
}
請求下一次 Vsync 信號
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;
}
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);
}
4栋盹、在doCallbacks()方法里面,
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
...
for (CallbackRecord c = callbacks; c != null; c = c.next) {
...
c.run(frameTimeNanos);
}
...
}
5敷矫、最后調(diào)用了
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) {
animation繪制
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
ViewRootImpl繪制
((Runnable)action).run();
}
}
}
ViewRootImpl繪制
1例获、上面是收到 VSync 信號后才開始繪制任務(wù)的流程,接下來講ViewRootImpl的繪制流程曹仗,剛才背景中提到
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
void scheduleTraversals() {
if (!mTraversalScheduled) {
添加同步屏障榨汤,保證 VSync 到來立即執(zhí)行繪制
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
交給Choreographer調(diào)度
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
后面開始mesure,layout怎茫,draw
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
}
2收壕、實際調(diào)用了
public final class Choreographer {
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
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);
}
}
}
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:
注冊VSYNC信號監(jiān)聽
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
注冊VSYNC信號監(jiān)聽,有回調(diào)
doScheduleCallback(msg.arg1);
break;
}
}
}
}
總結(jié)
1、插入同步屏障是為了保障vsync消息執(zhí)行的優(yōu)先級(繪制任務(wù)的優(yōu)先級在input和animation之后)
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.
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);
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);
}
}
}
2蜜宪、三緩存
雙緩存雖然可以有效的避免畫面的撕裂虫埂,但是當(dāng)?shù)?個 VSync 到來時GPU還在處理數(shù)據(jù),這時緩沖區(qū)在處理數(shù)據(jù)B圃验,被占用了掉伏,此時的VBlank階段就無法進行緩沖區(qū)交換,屏幕依然顯示前緩沖區(qū)的數(shù)據(jù)A损谦。此時只有等到下一個信號到來時岖免,此時 GPU 已經(jīng)處理完了,那么才可以交換緩沖區(qū)照捡,此時屏幕就會顯示交互后緩沖區(qū)的數(shù)據(jù)B了
3颅湘、可通過Choreographer.getInstance().postFrameCallback()來監(jiān)聽幀率情況