在看Animator源码的过程中,发现书上源码和实际源码不同,其handler的使用封装在了这个类中。后发现这个类是4.1之后才出现的,特此先找几篇文章记录一下其源代码及使用方式
在 Logcat 中使用关键词 Choreographer 进行过滤,对 App 做一些操作,一般能看到控制台输出这样的日志
I/Choreographer: Skipped 55 frames! The application may be doing too much work on its main thread.
I/Choreographer: Skipped 43 frames! The application may be doing too much work on its main thread.
这是观察 App 掉帧情况的一种手段
默认只会打印掉帧 >=30 帧的信息,已 ROOT 的手机可以修改此条件
getprop debug.choreographer.skipwarning //读取
setprop debug.choreographer.skipwarning 5 //修改
setprop ctl.restart surfaceflinger; setprop ctl.restart zygote //重启
理解 Choreographer 工作过程,有助于分析代码调用过程、进行帧率统计、分析 Systrace
硬件每 16 毫秒产一个 VSync 信号,App 要想实现垂直同步,就是要收到 VSync
只有调用 DisplayEventReceiver 的 nativeScheduleVsync 方法后,才能收到下一个 VSync
请求 VSync(即调用 nativeScheduleVsync)只是单次有效,并不是一劳永逸
请求 VSync → 收到 VSync → 请求 VSync → 收到 VSync
如果没有再次请求 VSync,则无法收到 VSync
我们将 Choreographer 工作过程分为 2 部分来分析:请求 VSync 和 收到 VSync
1.1 ViewRootImpl
[ViewRootImpl.java→]
void scheduleTraversals() {
...
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
熟悉 View 绘制过程的话,应该知道会一直递归向上到 ViewRootImpl 最终会调用到 scheduleTraversals()
在 ViewRootImpl 里也能找到
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, ...
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, ...
1.2 mChoreographer.postCallback
[Choreographer.java→]
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;
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);
}
}
}
把任务都放在 mCallbackQueues[callbackType] 队列中
1.3 scheduleFrameLocked
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
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()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
// mLastFrameTimeNanos 主要作用于当 USE_VSYNC = false 的时候,设置 doFrame 的时间
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
USE_VSYNC 默认是 true,也就是代表开启垂直同步
private static final boolean USE_VSYNC = SystemProperties.getBoolean(
"debug.choreographer.vsync", true);
// 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.
这句注释意思就是如果当前是在主线程,则立即执行 scheduleVsyncLocked(),如果不是主线程,则通过 mHandler 发消息给主线程,最终也是执行 scheduleVsyncLocked()
1.4 scheduleVsyncLocked
mDisplayEventReceiver = FrameDisplayEventReceiver(looper)
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable
[DisplayEventReceiver→]
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);
}
}
2.1 dispatchVsync
Java 层接收 VSync 的入口是 dispatchVsync(),看注释就知道是从 native 调用的
[DisplayEventReceiver.java→]
// Called from native code.
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
断点下就很清晰了,显然收到 VSync 时会往 UI Looper 中插入一个 msg,所以 onVsync 也是在主线程执行的
2.2 onVsync
[Choreographer.java→]
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
...
private long mTimestampNanos;
public FrameDisplayEventReceiver(Looper looper) {
super(looper);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
mTimestampNanos = timestampNanos;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
...
doFrame(mTimestampNanos, mFrame);
}
}
Message.obtain(mHandler, this) 所以 msg.callback 是 this,只要清楚 Handler 工作原理就知道最后会调用到 msg.callback.run(),也就是 FrameDisplayEventReceiver run(),进入 doFrame()
关注 mTimestampNanos,它是来自 onVsync 的 timestampNanos 参数,代表产生 VSync 的时间
2.3 doFrame
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
...
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;
// 修正 frameTimeNanos
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
// 请求 VSync
scheduleVsyncLocked();
return;
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
...
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 {
...
}
...
}
当出现掉帧时,会对 frameTimeNanos 进行修正,修正到最后一次 VSync 的时间
当 frameTimeNanos < mLastFrameTimeNanos 时,请求 VSync 然后 return,相当于忽略本次信号,等待下一个信号。但这个条件不太可能通过,有可能的情况比如 System.nanoTime() 发生倒退(比如修改手机时间)。反正这个条件通过就代表出现异常情况。
2.4 doCallbacks
void doCallbacks(int callbackType, long frameTimeNanos) {
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();
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.
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
if (jitterNanos >= 2 * mFrameIntervalNanos) {
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
+ mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
+ " ms which is more than twice the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Setting frame time to " + (lastFrameOffset * 0.000001f)
+ " ms in the past.");
mDebugPrintNextFrameTimeDelta = true;
}
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
extractDueCallbacksLocked 是取出执行时间在当前时间之前的所有 CallbackRecord,callbacks 是一个链表,然后遍历 callbacks 执行 run 方法
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
看到上面代码中有 FrameCallback.doFrame,只要 postFrameCallback(FrameCallback),下一次 Choreographer doFrame 时就会调用 FrameCallback.doFrame
下面的代码可以实现监听每一帧
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
// do something
Choreographer.getInstance().postFrameCallback(this);
}
});
doCallbacks 最后一步是 CALLBACK_COMMIT,如果 doCallbacks 耗时 >= 32 毫秒,就修正 frameTimeNanos
图中 doCallbacks 从 frameTimeNanos2 开始执行,执行到进入 CALLBACK_COMMIT 时,判断
now - frameTimeNanos >= 2 * mFrameIntervalNanos,于是把 frameTimeNanos 修正到倒数第二个信号时间
Choreographer 中涉及到 Looper 的有
这些 msg isAsynchronous() == true
msg.isAsynchronous() == true 表示是一个异步消息
当有 MessageQueue 设置 barrier 时,只有异步消息可以被处理,同步消息无法被处理,只有移除 barrier 后,同步消息才会被处理
如果没有设置 barrier,异步消息与同步消息没有区别
如何设置 barrier 和 移除 barrier,在 ViewRootImpl 里就可以看到
[ViewRootImpl.java→]
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
performTraversals();
...
}
}
写个 example 看下 msg 的调用顺序就知道 barrier 和异步消息是如何使用的
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("TEST", "" + msg.what);
if (msg.what == 3) {
removeSyncBarrier(mHandler, mBarrier);
mBarrier = -1;
}
}
};
private int mBarrier = -1;
@Override
public void onClick(View v) {
mBarrier = postSyncBarrier(mHandler);
//------------------------------------------------------------
mHandler.post(new Runnable() {
@Override
public void run() {
Log.d("TEST", "1");
}
});
//------------------------------------------------------------
Message msg = Message.obtain(mHandler, new Runnable() {
@Override
public void run() {
Log.d("TEST", "2");
}
});
mHandler.sendMessageDelayed(msg, 1000);
//------------------------------------------------------------
Message msg3 = Message.obtain();
msg3.setAsynchronous(true);
msg3.what = 3;
mHandler.sendMessageDelayed(msg3, 2000);
//------------------------------------------------------------
Message msg4 = Message.obtain();
msg4.setAsynchronous(true);
msg4.what = 4;
mHandler.sendMessageDelayed(msg4, 1500);
//------------------------------------------------------------
Message msg5 = Message.obtain();
msg5.what = 5;
mHandler.sendMessage(msg5);
}
public static int postSyncBarrier(Handler handler) {
MessageQueue messageQueue = handler.getLooper().getQueue();
try {
Method postSyncBarrier = messageQueue.getClass().getDeclaredMethod("postSyncBarrier");
postSyncBarrier.setAccessible(true);
return (int) postSyncBarrier.invoke(messageQueue);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
public static void removeSyncBarrier(Handler handler, int barrier) {
MessageQueue messageQueue = handler.getLooper().getQueue();
try {
Method removeSyncBarrier = messageQueue.getClass().getDeclaredMethod("removeSyncBarrier", int.class);
removeSyncBarrier.setAccessible(true);
removeSyncBarrier.invoke(messageQueue, barrier);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
输出顺序是 4 3 1 5 2
这整个过程只有异步消息可以被处理,Choreographer 过程中的动作也都是异步消息,这样可以确保 Choreographer 的顺利运转,也确保了第一时间执行 doTraversal(doTraversal → performTraversals 就是执行 view 的 layout、measure、draw),这个过程中如果有其他同步消息,也无法得到处理,都要等到 doTraversal 之后
下面这 2 行代码就没有 postSyncBarrier,显然没有 View 绘制的优先级高
[ViewRootImpl.java→]
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, ...
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, ...
以下图为例,分析执行过程
作者:风风风筝
链接:https://www.jianshu.com/p/dd32ec35db1d
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Github上有一个利用Choreographer原理实现的开源库TinyDancer,感兴趣的可以去学习一下。