0%

Android-Choreographer原理

基础知识

注:本文基于Android 10源码,为了文章的简洁性,引用源码的地方可能有所删减。

在一个典型的显示系统中,一般包括CPU、GPU、Display三个部分,CPU负责计算帧数据,把计算好的数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到buffer(图像缓冲区)里存起来,然后Display(屏幕或显示器)负责把buffer里的数据呈现到屏幕上。

相关基础概念见Android图形系统综述

Choreographer:编舞者,指对CPU/GPU绘制的指导,收到VSync信号才开始绘制,保证绘制拥有完整的16.6ms。通常应用层不会直接使用Choreographer,而是使用更高级的API,如View.invalidate()等,可以通过Choreographer来监控应用的帧率。

入口: scheduleTraversals

Android-Window机制原理可知,在调用startActivity后,经过AMS的一些处理,后面又通过Binder调用目标进程的ActivityThread.handleResumeActivity方法,在这个方法里会回调目标Activity的onResume和makeVisible方法,在makeVisible方法里完成WindowManager.addView的过程,这个过程调用了ViewRootImpl.setView方法,内部又调用其scheduleTraversals方法,最后会走到performTraversals方法,接着就到了熟悉的measure,layout,draw三大流程了。

另外,查看View.invalidate方法的源码,也可以发现最后会调用到ViewRootImpl.scheduleTraversals方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// ViewRootImpl
final ViewRootHandler mHandler = new ViewRootHandler();

void scheduleTraversals() {
if (!mTraversalScheduled) {
// 保证同时间多次更改只会刷新一次,例如TextView连续两次setText()也只会走一次绘制流程
mTraversalScheduled = true;
// 添加同步屏障,保证VSync到来立即执行绘制
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

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

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
// 真正执行View的measure,layout,draw流程
performTraversals();
}
}

首先使用mTraversalScheduled字段保证同时间多次更改只会刷新一次,然后为当前线程的MessageQueue添加同步屏障,来屏蔽同步消息,保证VSync到来后立即执行绘制,而不是要等前面的同步消息。调用mChoreographer.postCallback()方法发送了一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()–>performTraversals()–>绘制流程。具体逻辑接着往下看。

Choreographer实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// ViewRootImpl在WindowManager.addView时创建
public ViewRootImpl(Context context, Display display) {
// ...
mChoreographer = Choreographer.getInstance();
// ...
}

public final class Choreographer {
private static volatile Choreographer mMainInstance;

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!");
}
// VSYNC_SOURCE_APP = 0;
// VSYNC_SOURCE_SURFACE_FLINGER = 1;
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};

public static Choreographer getInstance() {
return sThreadInstance.get();
}
}

可知Choreographer和Looper一样都是线程单例的,由ThreadLocal实现,见ThreadLocal原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public final class Choreographer {
// 4.1以上默认是true
// Enable/disable vsync for animations and drawing.
private static final boolean USE_VSYNC = SystemProperties.getBoolean("debug.choreographer.vsync", true);
public static final int CALLBACK_COMMIT = 3;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;

private long mLastFrameTimeNanos;
private long mFrameIntervalNanos;

// VSync事件接收器
private final FrameDisplayEventReceiver mDisplayEventReceiver;

private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
// 使用当前线程looper创建Handler
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null;
mLastFrameTimeNanos = Long.MIN_VALUE;
// 计算一帧的时间,即16ms
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));
}
}

// Android手机屏幕的刷新频率是60Hz,具体实现可以查看源码
private static float getRefreshRate() {
// getDisplayInfo方法是通过Binder调用DisplayManagerService相关接口
DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(Display.DEFAULT_DISPLAY);
return di.getMode().getRefreshRate();
}

Choreographer中共有四种callbackType:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static final String[] CALLBACK_TRACE_TITLES = {"input", "animation", "traversal", "commit"};

// Input callback. Runs first. 输入事件
public static final int CALLBACK_INPUT = 0;
// Animation callback. Runs before traversals. 动画
public static final int CALLBACK_ANIMATION = 1;
// Traversal callback. Handles layout and draw. Runs after all other asynchronous messages have been handled.
// 窗口刷新,执行measure/layout/draw操作
public static final int CALLBACK_TRAVERSAL = 2;
// Commit callback. Handles post-draw operations for the frame. Runs after traversal completes.
public static final int CALLBACK_COMMIT = 3;

private static final int CALLBACK_LAST = CALLBACK_COMMIT;

这四种类型的任务存入对应类型的CallbackQueue中,每当收到VSYNC信号时,Choreographer将按顺序处理这些类型的任务。

下面看看FrameHandler的源码,它用来处理异步消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
}
}

Vsync信号注册

DisplayEventReceiver

mDisplayEventReceiver 是 FrameDisplayEventReceiver 类型的实例,在Choreographer构造方法中实例化,其父类为 DisplayEventReceiver。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class DisplayEventReceiver {
public static final int VSYNC_SOURCE_APP = 0;
private long mReceiverPtr;

public DisplayEventReceiver(Looper looper, int vsyncSource) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mMessageQueue = looper.getQueue();
// 注册VSYNC信号监听者
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, vsyncSource);
mCloseGuard.open("dispose");
}

private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver, MessageQueue messageQueue, int vsyncSource);
}

nativeInit

nativeInit是一个native方法,其实现在frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp中:

1
2
3
4
5
6
7
8
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject messageQueueObj, jint vsyncSource) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource);
status_t status = receiver->initialize();
receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}

NativeDisplayEventReceiver 继承自 DisplayEventDispatcher:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != NULL) {
mEventConnection = sf->createDisplayEventConnection(vsyncSource);
if (mEventConnection != NULL) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
}
}
}

sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource) {
if (vsyncSource == eVsyncSourceSurfaceFlinger) {
return mSFEventThread->createEventConnection();
} else {
// vsyncSource 是 APP
return mEventThread->createEventConnection();
}
}

Android-SurfaceFlinger启动与绘图原理 可以知道 EventThread.createEventConnection 创建了一个对 Vsync 信号感兴趣的连接。initialize 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// frameworks/base/libs/androidfw/DisplayEventDispatcher.cpp
status_t DisplayEventDispatcher::initialize() {
// DisplayEventReceiver mReceiver;
status_t result = mReceiver.initCheck();
int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
if (rc < 0) {
return UNKNOWN_ERROR;
}
return OK;
}

int DisplayEventReceiver::getFd() const {
if (mDataChannel == NULL)
return NO_INIT;
// 返回 BitTube 中的 mReceiveFd 描述符
return mDataChannel->getFd();
}

mReceiver 是 DisplayEventReceiver 类型实例,位于frameworks/native/libs/gui/DisplayEventReceiver.cppmLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL) 用来监听 mReceiver 所获取的文件句柄,当有存在对 Vsync 信号感兴趣的连接接收到了 Vsync 信号时,会发送数据到 mReceiver, 然后回调 DisplayEventDispatcher 中的 handleEvent 方法,具体源码参考 Android-SurfaceFlinger启动与绘图原理 中 addFd 的解析。

Looper.addFd

BitTube

先看一下 BitTube, 这是 Android 提供的一种进程通讯方式,它通过 socketpair 来现全双工的通讯。在 BitTube 对象中有两个文件描述符: mReceiveFd 和 mSendFd, 以及 read 和 write 方法分别用来进行读写操作。获取两个文件描述符的函数:

1
2
3
4
5
6
7
int BitTube::getFd() const {
return mReceiveFd;
}

int BitTube::getSendFd() const {
return mSendFd;
}

跨进程

上面 Choreographer 中的 mEventConnection 是 IDisplayEventConnection 类型,是一个 native 层的 Binder 代理对象,用来跟 SurfaceFlinger 跨进程通信,可以看到它通过 stealReceiveChannel 方法把 APP 进程端创建的 BitTube 指针传给了 SF 进程。

然后在 APP 进程又通过 Looper->addFd(mReceiver.getFd(), ...) 方法监听 mReceiveFd 描述符的数据,当 Vsync 信号到来后,SF 对 BitTube 做 send 操作,则 Looper 监听到对应 mReceiveFd 中的数据变化,于是触发 DisplayEventDispatcher.handleEvent 方法,该方法中会调用 mReceiver(DisplayEventReceiver).getEvents 方法,这个方法会调用 BitTube.recvObjects 拿到 SF 进程传来的数据(由于 Looper 触发了监听,此时 recvObjects 肯定能拿到数据,而不会阻塞),即表示 Vsync 信号来了。

请求Vsync信号

Choreographer.postCallback

接着看一下上面调用的代码:mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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) {
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);
}

private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
// 对应类型的CallbackQueue添加Callback
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);
// handleMessage会调用doScheduleCallback(msg.arg1)方法
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}

void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}

可以看到延迟与否都会调用 scheduleFrameLocked 方法。

Choreographer.scheduleFrameLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
// Android 4.1后默认开启
if (USE_VSYNC) {
// Looper.myLooper() == mLooper
if (isRunningOnLooperThreadLocked()) {
// 当前执行的线程是mLooper所在线程
scheduleVsyncLocked();
} else {
// 否则通过handleMessage会调用doScheduleVsync方法
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
// private static final long DEFAULT_FRAME_DELAY = 10;
// private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
// 会调用doFrame(System.nanoTime(), 0)方法
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}

void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}

private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}

DisplayEventReceiver.scheduleVsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public void scheduleVsync() {
if (mReceiverPtr == 0) {
// ...
} else {
nativeScheduleVsync(mReceiverPtr);
}
}

// frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
sp<NativeDisplayEventReceiver> receiver = reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
status_t status = receiver->scheduleVsync();
// ...
}

// frameworks/base/libs/androidfw/DisplayEventDispatcher.cpp
status_t DisplayEventDispatcher::scheduleVsync() {
if (!mWaitingForVsync) {
// Drain all pending events.
nsecs_t vsyncTimestamp;
int32_t vsyncDisplayId;
uint32_t vsyncCount;
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
}

status_t status = mReceiver.requestNextVsync();
mWaitingForVsync = true;
}
return OK;
}

// frameworks/native/libs/gui/DisplayEventReceiver.cpp
status_t DisplayEventReceiver::requestNextVsync() {
if (mEventConnection != NULL) {
// 请求接收下一次Vsync信号的回调
mEventConnection->requestNextVsync();
return NO_ERROR;
}
return NO_INIT;
}

关于 requestNextVsync 的逻辑已经在 Android-SurfaceFlinger启动与绘图原理 中解析过了,它用来请求接收下一次 Vsync 信号,可以唤醒 EventThread 线程,等到 Vsync 信号到来后回调给 APP。

Vsync回调流程

DisplayEventDispatcher::handleEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
// Drain all pending events, keep the last vsync.
nsecs_t vsyncTimestamp;
int32_t vsyncDisplayId;
uint32_t vsyncCount;
// 清除所有的pending事件,只保留最后一次vsync
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
mWaitingForVsync = false;
// 分发Vsync,实现方法在子类中
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
return 1; // keep the callback
}

void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
}
mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}

int register_android_view_DisplayEventReceiver(JNIEnv* env) {
// ...
jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JII)V");
return res;
}

由上可知,会调用到Java层 android/view/DisplayEventReceiver 的 dispatchVsync 方法:

1
2
3
4
5
6
7
8
public abstract class DisplayEventReceiver {
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}

public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
}
}

FrameDisplayEventReceiver.onVsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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) {
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
// 非主display
Log.d(TAG, "Received vsync from secondary display, but we don't support "
+ "this case yet. Choreographer needs a way to explicitly request "
+ "vsync for a specific display to ensure it doesn't lose track "
+ "of its scheduled vsync.");
scheduleVsync();
return;
}

long now = System.nanoTime();
if (timestampNanos > now) {
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;
// 会调用run方法
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);
}
}

Choreographer.doFrame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt("debug.choreographer.skipwarning", 30);

void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
// 计划执行时间
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
// 是否超过一帧的时间,因为虽然添加了同步屏障,但是还是有正在执行的同步任务,导致doFrame延迟执行了
// 计算掉帧数
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
// 默认掉帧超过30帧打印日志
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;
}

if (frameTimeNanos < mLastFrameTimeNanos) {
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 {
// 按类型执行
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);
} finally {
AnimationUtils.unlockAnimationClock();
}
}

Choreographer.doCallbacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
// 根据指定的类型CallbackkQueue中查找到达执行时间的CallbackRecord
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
if (jitterNanos >= 2 * mFrameIntervalNanos) {
// 当commit类型回调执行的时间点超过2帧,则更新mLastFrameTimeNanos
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos + mFrameIntervalNanos;
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
// 迭代执行队列所有任务
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);
}
}
}

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

Choreographer.postFrameCallback

用法

postFrameCallback方法通常用来计算丢帧情况,用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Application.java
public void onCreate() {
super.onCreate();
Choreographer.getInstance().postFrameCallback(new FPSFrameCallback(System.nanoTime()));
}

public class FPSFrameCallback implements Choreographer.FrameCallback {

private static final String TAG = "FPS_TEST";
private long mLastFrameTimeNanos = 0;
private long mFrameIntervalNanos;

public FPSFrameCallback(long lastFrameTimeNanos) {
mLastFrameTimeNanos = lastFrameTimeNanos;
mFrameIntervalNanos = (long)(1000000000 / 60.0);
}

@Override
public void doFrame(long frameTimeNanos) {
// 初始化时间
if (mLastFrameTimeNanos == 0) {
mLastFrameTimeNanos = frameTimeNanos;
}
final long jitterNanos = frameTimeNanos - mLastFrameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if(skippedFrames > 30){
// 丢帧30以上打印日志
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
}
mLastFrameTimeNanos = frameTimeNanos;
// 注册下一帧回调
Choreographer.getInstance().postFrameCallback(this);
}
}

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);
}

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
// 也是调用postCallbackDelayedInternal方法
// callbackType是CALLBACK_ANIMATION类型
// token是FRAME_CALLBACK_TOKEN,action是FrameCallback
postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

public interface FrameCallback {
void doFrame(long frameTimeNanos);
}

总结

  • Choreographer: 使 CPU/GPU 的绘制是在 VSYNC 到来时开始。Choreographer 初始化时会创建一个表示对 Vsync 信号感兴趣的连接,当有绘制请求时通过 postCallback 方法请求下一次 Vsync 信号,当信号到来后才开始执行绘制任务。
  • 只有当 App 注册监听下一个 Vsync 信号后才能接收到 Vsync 到来的回调。如果界面一直保持不变,那么 App 不会去接收每隔 16.6ms 一次的 Vsync 事件,但底层依旧会以这个频率来切换每一帧的画面(也是通过监听 Vsync 信号实现)。即当界面不变时屏幕也会固定每 16.6ms 刷新,但 CPU/GPU 不走绘制流程。
  • 当 View 请求刷新时,这个任务并不会马上开始,而是需要等到下一个 Vsync 信号到来时才开始;measure/layout/draw 流程运行完后,界面也不会立刻刷新,而会等到下一个 VSync 信号到来时才进行缓存交换和显示。
  • 造成丢帧主要有两个原因:一是遍历绘制 View 树以及计算屏幕数据超过了16.6ms;二是主线程一直在处理其他耗时消息,导致绘制任务迟迟不能开始(同步屏障不能完全解决这个问题)。
  • 可通过Choreographer.getInstance().postFrameCallback()来监听帧率情况。

阅读这篇文章建议先阅读 SurfaceFlinger 启动与工作流程 这篇文章,然后结合 Choreographer 的工作流程,可以对 Vsync 信号是怎么协调 App 端的绘制任务以及 SurfaceFlinger 的合成任务有一个比较清晰的认识。

用一张图总结一下 Choreographer 的工作流程:

Choreographer工作流程