/**
* Callback type: Input callback. Runs first.
* @hide
*/
public static final int CALLBACK_INPUT = 0;
/**
* Callback type: Animation callback. Runs before traversals.
* @hide
*/
public static final int CALLBACK_ANIMATION = 1;
/**
* Callback type: Traversal callback. Handles layout and draw. Runs last
* after all other asynchronous messages have been handled.
* @hide
*/
public static final int CALLBACK_TRAVERSAL = 2;
private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;
Choreographer, 黄油计划引入的协调器,实现了帧(就是Frame,因为有了垂直同步,所以可能Frame_callback是定期的)同步的功能, 负责同意协调所有的task的运行。
Choreographer 所支持的三种Task, 运行顺序: input > animation > traversal.
Choreographer 的getInstance() 返回的是 ThreadLocal的实例, 每个线程都有自己的Choreographer, 并且Choreographer使用的handler所依附也是该线程:
可以看到 Looper.myLooper()
// Thread local storage for the choreographer.
private static final ThreadLocal sThreadInstance =
new ThreadLocal() {
@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);
}
};
private Choreographer(Looper looper) {
mLooper = looper;
mHandler = new FrameHandler(looper);
.....................
}
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;
}
}
}
注意上面的三类msg,处理的逻辑是不一样的:
doScheduleVsync() 和 doScheduleCallback()都没有真正的干活,
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
比如doScheduleCallback: 先检查有没有Frame的CallBack已经被schedue了<如果有的话,在不久这些被post进来的task就可以被执行了,不需要再schedule一次>,如果没有,再看看当前的callBackQueue的第一个callback是否已经到期要执行.
如果到期要执行,那么就schedule一个 Frame的Callback:
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
如果使用了V同步:
if (USE_VSYNC) {
if (DEBUG) {
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.
如果当前线程就是该Choreographer所属的线程
if (isRunningOnLooperThreadLocked()) {
直接schedule一个V同步.
scheduleVsyncLocked();
做的事情:
mDisplayEventReceiver.scheduleVsync();
>
/**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
*/
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);
}
}
onVsync()是收到了Vsync pulse之后的响应:
/**
* Called when a vertical sync pulse is received.
* The recipient should render a frame and then call {@link #scheduleVsync}
* to schedule the next vertical sync pulse.
*
* @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
* timebase.
* @param builtInDisplayId The surface flinger built-in display id such as
* {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}.
* @param frame The frame number. Increases by one for each vertical sync interval.
*/
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
}
而Vsync里传递来的timestampNanos,就是本次pulse的时间戳。
而FrameDisplayEventReceiver的onVsync()函数做的事情是将作为runnable的自己send到所处的线程中去,注意是sendMessageAtTime,这里的时间是
这样计算的,如果timestampNanos > now, 那么timestampNanos = now.
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
而doFrame()函数才是真正的做了很多工作:
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
如果没有schedule过一个FrameCallBack,直接返回.mFrameScheduled只有在scheduleFrameLocked()才会是true
if (!mFrameScheduled) {
return; // no work to do
}
startNanos = System.nanoTime();
计算一下抖动延迟:startNanos 是当前的时间,而frameTimeNanos是该Frame计划开始的时间
final long jitterNanos = startNanos - frameTimeNanos;
如果抖动延迟超过了某个阈值
if (jitterNanos >= mFrameIntervalNanos) {
大概计算一下跳帧的严重程度
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
如果跳帧跳的不能忍了,警告一下,是不是UI线程的工作太多了.
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) {
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.");
}
重新规划一下该Frame开始的时间
frameTimeNanos = startNanos - lastFrameOffset;
}
如果这一帧要开始的时间比上一帧开始的时间都晚,那么直接跳过此次,等待下一次的Vsync来触发了.
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
scheduleVsyncLocked();
return;
}
这一帧开始了,那么就没有被schedule的帧了
mFrameScheduled = false;
更新上一帧开始的时间
mLastFrameTimeNanos = frameTimeNanos;
}
这时候才开始依次执行之前post给自己的各种Task,注意执行的顺序就是最开始说的顺序:
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
打印出画这一帧所花的时间:
if (DEBUG) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
postCallbackDelayedInternal负责把MSG_DO_SCHEDULE_CALLBACK的Task, post到该Choreographer实例所属线程的handler中, 通过Message的形式,这个函数其实也是是view的requestLayout等操作,最终使ViewRootImpl调用的:
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + 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);
}
}
}
关于这个类注释说的也很清晰了:
/**
* Coordinates the timing of animations, input and drawing.
*
* 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.
*
* 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.
*
*
* - To post an animation to be processed on a regular time basis synchronized with
* display frame rendering, use {@link android.animation.ValueAnimator#start}.
* - To post a {@link Runnable} to be invoked once at the beginning of the next display
* frame, use {@link View#postOnAnimation}.
* - To post a {@link Runnable} to be invoked once at the beginning of the next display
* frame after a delay, use {@link View#postOnAnimationDelayed}.
* - 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)}.
* - 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.
*
*
* 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.
*
*
* - 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}.
* - ... and that's about it.
*
*
* 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.
*
*/
最终还是一个Vsync触发了doFrame,来进行操作的. 不过如果之前已经有一个Frame将要执行了,那么就不需要再schedule一个,直接等待这个Frame的来临即可.
From:http://www.360doc.com/content/14/0329/00/10366845_364576441.shtml
所有的图像显示输出都是由时钟驱动的,这个驱动信号称为VSYNC。这个名词来源于模拟电视时代,在那个年代,因为带宽的限制,每一帧图像都有分成两次传输,先扫描偶数行(也称偶场)传输,再回到头部扫描奇数行(奇场),扫描之前,发送一个VSYNC同步信号,用于标识这个这是一场的开始。场频,也就是VSYNC 频率决定了帧率(场频/2). 在现在的数字传输中,已经没有了场的概念,但VSYNC这一概念得于保持下来,代表了图像的刷新频率,意味着收到VSYNC信号后,我们必须将新的一帧进行显示。
VSYNC一般由硬件产生,也可以由软件产生(如果够准确的话),Android 中VSYNC来着于HWComposer,接收者没错,就是Choreographer。Choreographer英文意思是编舞者,跳舞很讲究节奏不是吗,必须要踩准点。Choreographer 就是用来帮助Android的动画,输入,还是显示刷新按照固定节奏来完成工作的。
从图中我们可以看到, Choreographer 是ViewRootImpl 创建的(Choreographer是一个sigleton类,第一个访问它的ViewRootImpl创建它),它拥有一个Receiver, 用来接收外部传入的Event,它还有一个Callback Queue, 里面存放着若干个CallbackRecord, 还有一个FrameHandler,用来handleMessage, 最后,它还跟Looper有引用关系。再看看下面这张时序图,一切就清楚了,
首先Looper调用loop() 后,线程进入进入睡眠,直到收到一个消息。Looper也支持addFd()方法,这样如果某个fd上发生了IO操作(read/write), 它也会从睡眠中醒来。Choreographer的实现用到了这两种方式,首先他通过某种方式获取到SurfaceFlinger 进程提供的fd,然后将其交给Looper进行监听,只要SurfaceFlinger往这个fd写入VSync事件,looper便会唤醒。Lopper唤醒后,会执行onVsync()时间,这里面没有做太多事情,而是调用Handler接口 sendMessageAtTime() 往消息队列里又送了一个消息。这个消息最终调到了Handler (实际是FrameHandler)的handleCallback来完成上层安排的工作。为什么要绕这么大个圈?为什么不在onVSync里直接handleCallback()? 毕竟onVSync 和 handleCallback() 都在一个线程里。这是因为MessageQueue 不光接收来自SurfaceFlinger 的VSync 事件,还有来自上层的控制消息。VSync的处理是相当频繁的,如果不将VSync信号送人MessageQueue进行排队,MessageQueue里的事件就有可能得不到及时处理,严重的话会导致溢出。当然了,如果因为VSync信号排队而导致处理延迟,这就是设计的问题了,这也是为什么Android文档里反复强调在Activity的onXXX()里不要做太耗时的工作,因为这些回调函数和Choreographer运行在同一个线程里,这个线程就是所谓的UI线程。
言归正传,继续往前,VSync事件最终在doFrame()里调了三次doCallbacks()来完成不同的功能, 分别处理用户输入事件,动画刷新(动画就是定时更新的图片), 最后执行performTraversals(),这个函数里面主要是检查当前窗口当前状态,比如说是否依然可见,尺寸,方向,布局是否发生改变(可能是由前面的用户输入触发的),分别调用performMeasure(), performLayout, performDraw()完成测量,布局和绘制工作。