想要了解Choreographer ,就需要知道他是干什么的,并且需要知道知道他内部主要成员有哪些,他们都分别负责什么.下面我先来简单介绍一下Choreographer以及他主要的成员变量
Choreographer
Choreographer 主要负责接受vsycs 垂直同步信号,并且在垂直同步信号来临时,将各个需要响应的绘制事件依照先后顺序一次执行,
成员变量 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();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
FrameHandler 就是一个通过looper 来创建的Handler ,响应各个事件
成员变量 FrameDisplayEventReceiver接收到垂直同步信号后,使用 FrameHandler 来响应垂直同步事件
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);
}
// 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 来获得接收垂直同步信号的能力,同时他也实现了Runnable 接口,可以让他在接收到垂直同步信号的消息时使用FrameHandler 来发送一个自身的callback,也就是 run 中的方法开始刷新工作
成员变量 CallbackQueue[] 是一个数组,数组中的每一条数据时一个链表,记录着链表头,当有新的callback 加入到链表中时,根据时间来决定链表的插入顺序
Choreographer 为 CallbackQueue 定义了一个优先级,他们分别是
/**
* Callback type: Input callback. Runs first.
* @hide
*/
public static final int CALLBACK_INPUT = 0;
/**
* Callback type: Animation callback. Runs before {@link #CALLBACK_INSETS_ANIMATION}.
* @hide
*/
@TestApi
public static final int CALLBACK_ANIMATION = 1;
/**
* Callback type: Animation callback to handle inset updates. This is separate from
* {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
* {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
* update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
* that contains all the combined updated insets.
* <p>
* Both input and animation may change insets, so we need to run this after these callbacks, but
* before traversals.
* <p>
* Runs before traversals.
* @hide
*/
public static final int CALLBACK_INSETS_ANIMATION = 2;
/**
* Callback type: Traversal callback. Handles layout and draw. Runs
* after all other asynchronous messages have been handled.
* @hide
*/
public static final int CALLBACK_TRAVERSAL = 3;
那么在 CallbackQueue[] 这个数组中的顺序应该就是
CallbackQueue[0] input 队列
CallbackQueue[1] 动画队列
CallbackQueue[2] 添加动画队列
CallbackQueue[3] TRAVERSAL ,也就是在ViewRootImpl 中发送完消息屏障后,发送的callback ,
image.png
为什么说 CallbackQueue 是一个链表看下面的代码
image.png一个很经典的链表插入操作,与MessageQueue类似,
下面我们再来看看 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();
}
// Thread local storage for the SF choreographer.
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);
}
};
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();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
从代码中可以看出来Choreographer 的创建和Looper类似,都是一个线程中只能有一个, 都保存在ThreadLocal中,
他的初始化过程中创建了 FrameHandler 和 FrameDisplayEventReceiver ,并且根据 getRefreshRate 帧率计算了刷新间隔,创建了一个 CallbackQueue 队列数组,
那么整个链路我们串起来看就应该是这样的,
1.ViewRootImpl 发送了一个消息屏障,并且在Choreographer中插入了一条 优先级为 CALLBACK_TRAVERSAL 的callback,用来执行测量 布局 绘制等工作
2. Choreographer 中的 FrameDisplayEventReceiver 接收到垂直同步信号的时候,先进行时间修正,将自身作为callback,使用 FrameHandler 发送一个异步消息,来执行自身的 run 方法,也就是 doFream() 方法
3. doFream 方法
@UnsupportedAppUsage
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
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();
//计算时间差
final long jitterNanos = startNanos - frameTimeNanos;
//如果时间差大于每一帧的时间间隔,那么就证明丢帧了
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;
}
// 如果需要执行的帧的执行时机已经已经执行完毕,那么需要重新等待下一个信号的到来
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;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
//按照实现约定好的顺序执行 CallbackQueue 中的callback
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.");
}
}
这个方法先获取了一下系统时间,并且与这一帧的执行时间做比较,如果他们的差值大于每一帧的时间,那么证明丢帧了同时会在控制台打印 Skipped Frames log,
修正一下时间,如果执行时间比上一次已经执行的时间小,证明当前这一帧的是一个无用帧了,返回 ,
将这一帧的执行时间作为最后一帧的执行时间,
依次执行 CallbackQueue 链表数据中的callback,
这一帧结束
网友评论