Choreographer顾名思义编舞者,用来接收硬件发送的Vsync (垂直同步)信号。Android系统每隔16ms会发出Vsync信号,来通知重绘、渲染,每一帧渲染会回调doFrame(frameTimeNanos: Long)
Choreographer通常用来计算掉帧情况
fun start() {
Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
var lastFrameTimeNanos: Long = 0
override fun doFrame(frameTimeNanos: Long) {
//上次回调时间
if (lastFrameTimeNanos == 0L) {
lastFrameTimeNanos = frameTimeNanos
//注册下一帧回调
Choreographer.getInstance().postFrameCallback(this)
return
}
val diff = (frameTimeNanos - lastFrameTimeNanos) / 1000000
if (diff > 16.6f) {
//掉帧数
val droppedFrameCount = (diff / 16.6).toInt()
}
lastFrameTimeNanos = frameTimeNanos
//注册下一帧回调
Choreographer.getInstance().postFrameCallback(this)
}
})
}
Choreographer通过ThreadLocal实现线程单例,每个线程独有。
Choreographer初始化
/**
* Gets the choreographer for the calling thread. Must be called from
* a thread that already has a {@link android.os.Looper} associated with it.
*
* @return The choreographer for this thread.
* @throws IllegalStateException if the thread does not have a looper.
*/
public static Choreographer getInstance() {
return sThreadInstance.get();
}
//Choreographer是线程单例,每个线程独有(即线程间不共享)
// Thread local storage for the choreographer.
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
//获取当前线程的Looper
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
//创建Choreographer
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
//1.初始化FrameHandler(消息处理的Handler)
mHandler = new FrameHandler(looper);
//2.初始化DisplayEventReceiver,用来接收Vsync信号
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
//记录上一次收到Vsync信号的时间(单位:纳米)
mLastFrameTimeNanos = Long.MIN_VALUE;
//定义一帧的时间,Android屏幕60HZ的刷新频率,就是16ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//数组中有5个链表,每个链表存放input、animation、traversal事件的callback回调
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));
}
Choreographer通过ThreadLocal实现线程单例,每个线程独有。
其内部维护了一个数组mCallbackQueues,每个数组下标都是一个链表CallbackQueue(链表中的元素按照时间从小到大排序)。
//输入事件,首先执行
public static final int CALLBACK_INPUT = 0;
//动画,第二执行
public static final int CALLBACK_ANIMATION = 1;
//插入更新的动画,第三执行
public static final int CALLBACK_INSETS_ANIMATION = 2;
//绘制,第四执行
public static final int CALLBACK_TRAVERSAL = 3;
//提交,最后执行
public static final int CALLBACK_COMMIT = 4;
五种类型的任务存入对应的链表CallbackQueue,每当收到Vsync信号时,Choreographer首先依次INPUT、ANIMATION、TRAVERSAL类型任务。
外部通过postCallback去安排任务,将任务放在对应下标链表,调用一次只会接收一次信号,接收到信号处理完逻辑循环调用。
postCallback调用了postCallbackDelayed,接着调用了postCallbackDelayedInternal
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;
//把dueTime, action, token组装成CallbackRecord存入对应类型链表的下一个节点
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
//无延迟则立即执行
scheduleFrameLocked(now);
} else {
//有延迟则通过mHandler发送MSG_DO_SCHEDULE_CALLBACK消息,最终也走到scheduleFrameLocked
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
//设置异步消息,优先执行
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
接着看下FrameHandler对MSG_DO_SCHEDULE_CALLBACK消息的处理
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(即绘制过程)
doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
//申请Vsync信号(当前需要绘制任务时)
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
//通过Handler发送的延迟任务,也走到了scheduleFrameLocked
doScheduleCallback(msg.arg1);
break;
}
}
}
延迟执行也走到了scheduleFrameLocked
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
//4.1及以上默认开启了Vsync
if (USE_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()) {
//在原线程申请Vsync信号
scheduleVsyncLocked();
} else {
//不在则用mHandler发送消息到原线程,最终还是走到scheduleVsyncLocked
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
//未开启Vsync直接走doFrame(通过mHandler发送MSG_DO_FRAME消息)
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);
}
}
}
接着看下scheduleVsyncLocked是如何申请Vsync信号的:
private final FrameDisplayEventReceiver mDisplayEventReceiver;
private void scheduleVsyncLocked() {
//调用父类的scheduleVsync方法
mDisplayEventReceiver.scheduleVsync();
}
mDisplayEventReceiver是在Choreographer的构造方法中创建的,其父类DisplayEventReceiver注册了Vsync信号监听:
public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mMessageQueue = looper.getQueue();
//通过JNI注册Vsync信号监听
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource, eventRegistration);
}
@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 {
//scheduleVsync使用native方法去申请Vsync信号,回调是onVsync
nativeScheduleVsync(mReceiverPtr);
}
}
构造DisplayEventReceiver时使用native方法注册了Vsync信号监听,native方法去申请了Vsync信号,回调到了
onVsync,具体实现在FrameDisplayEventReceiver的onVsync:
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, 0);
}
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
//...
//将自身的Runable传入msg,会执行下面的run方法,即执行doFrame
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, mLastVsyncEventData);
}
}
接收到Vsync信号回调后,使用mHandler发送异步消息到MessageQueue,执行run方法的doFrame
void doFrame(long frameTimeNanos, int frame, DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
try {
synchronized (mLock) {
if (!mFrameScheduled) {
traceMessage("Frame not scheduled");
return; // no work to do
}
//预期时间,即Vsync信号发出的时间
long intendedFrameTimeNanos = frameTimeNanos;
//System.nanoTime()此方法提供纳秒级精度,常用于测量运行时间
startNanos = System.nanoTime();
//计算执行的时间差(虽然MessageQueue添加了同步屏障,但是当前有正在执行的同步任务,还是会导致doFrame延迟)
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= frameIntervalNanos) {
//计算掉帧数
final long skippedFrames = jitterNanos / frameIntervalNanos;
//掉帧数 >=30打印日志
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 % frameIntervalNanos;
//进行绘制时间修正,保证每次绘制时间的间隔正确
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.");
}
traceMessage("Frame time goes backward");
//跳过本次绘制,请求下一帧
scheduleVsyncLocked();
return;
}
//控制绘制频率
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
traceMessage("Frame skipped due to FPSDivisor");
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
//重置标志位,可以再次进入scheduleFrameLocked
mFrameScheduled = false;
//记录最后一帧的时间
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
mLastVsyncEventData = vsyncEventData;
}
//下面就是按照类型顺序执行任务
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
//1.INPUT类型任务
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
//2.ANIMATION类型任务
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
//3.INSETS_ANIMATION类型任务
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
frameIntervalNanos);
//4.TRAVERSAL类型任务
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
//5.COMMIT类型任务
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
可以看到,Choreographer的引入,主要是配合Vsync,为上层App的渲染提供了稳定的时机,统一管理应用的输入、动画、绘制等任务的执行。
接着看任务的执行doCallbacks方法:
void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
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();
/**
*
* 找到当前这一帧需要消费的callback回调,因为CallbackQueue是链表,返回头节点即可
* - 根据callbackType类型从mCallbackQueues数组中获取对应的链表
* - 传入当前时间作为参考点,从头遍历,找到第一个晚于当前时间的callback,将它设置为新的头结点,并断开这个新的头节点与前一个节点,返回之前的头结点
*
**/
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.
//CALLBACK_COMMIT是用来更新帧时间的,确保
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
//延迟到达2帧及以上时,才会更新帧时间,确保提交时间始终至少落后一帧来确保安全
if (jitterNanos >= 2 * frameIntervalNanos) {
final long lastFrameOffset = jitterNanos % frameIntervalNanos
+ frameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
+ " ms which is more than twice the frame interval of "
+ (frameIntervalNanos * 0.000001f) + " ms! "
+ "Setting frame time to " + (lastFrameOffset * 0.000001f)
+ " ms in the past.");
mDebugPrintNextFrameTimeDelta = true;
}
//更新时间
frameTimeNanos = now - lastFrameOffset;
//记录当前最后一帧的时间
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
//执行CallbackRecord的run方法
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
//回收callbacks
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
}
}
接着看下CallbackRecord的run方法
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
//Choreographer的postFrameCallback()中会传入token为FRAME_CALLBACK_TOKEN,会走到这里,通常用来计算丢帧情况
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
//取出Runnable执行run()
((Runnable)action).run();
}
}
}
ViewRootImp中就使用了Choreographer:
ActivityThread#handleResumeActivity -> wm.addView
-> WindowManagerImpl#addView -> mGlobal.addView
-> WindowManagerGlobal#addView -> root.setView
-> ViewRootImpl#setView -> requestLayout() -> scheduleTraversals()
Activity启动走完onResume后,会进行Window的添加,Window添加过程中会调用ViewRootImpl的requestLayout()方法来请求绘制布局,requestLayout()方法内部又走到了scheduleTraversals()方法,先来看下这个方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
//标志位置为true,防止短时间内重复调用,View开始绘制渲染新页面之前恢复标志位。
mTraversalScheduled = true;
//添加同步屏障,保证Vsync到来立即执行刷新
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//调用Choreographer.postCallback,发送一个会在下一帧执行的回调,执行TraversalRunnable任务
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//开始三大绘制流程
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
ViewRootImp中使用了Choreographer.postCallback()发送任务到队列中,监听Vsync信号,当有Vsync信号到来时,使用Handler发送异步消息,执行Runable任务。