2024-12-23

49 阅读7分钟

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

...

//开始三大绘制流程

performTraversals();

...

}

}

主要有以下逻辑:

  1. 首先使用mTraversalScheduled字段保证同时间多次更改只会刷新一次,例如TextView连续两次setText(),也只会走一次绘制流程。

  2. 然后把当前线程的消息队列Queue添加了同步屏障,这样就屏蔽了正常的同步消息,保证VSync到来后立即执行绘制,而不是要等前面的同步消息。后面会具体分析同步屏障和异步消息的代码逻辑。

  3. 调用了mChoreographer.postCallback()方法,发送一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()—>performTraversals()–>绘制流程

接下来,就是分析的重点——Choreographer。我们先看它的实例mChoreographer,是在ViewRootImpl的构造方法内使用Choreographer.getInstance()创建:

Choreographer mChoreographer;

//ViewRootImpl实例是在添加window时创建

public ViewRootImpl(Context context, Display display) {

...

mChoreographer = Choreographer.getInstance();

...

}

我们先来看看Choreographer.getInstance():

public static Choreographer getInstance() {

return sThreadInstance.get();

}

private static final ThreadLocal sThreadInstance =

new ThreadLocal() {

@Override

protected Choreographer initialValue() {

Looper looper = Looper.myLooper();

if (looper == null) {

//当前线程要有looper,Choreographer实例需要传入

throw new IllegalStateException("The current thread must have a looper!");

}

Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);

if (looper == Looper.getMainLooper()) {

mMainInstance = choreographer;

}

return choreographer;

}

};

看到这里 如你对Handler机制中looper比较熟悉的话,应该知道 Choreographer和Looper一样 是线程单例的。且当前线程要有looper,Choreographer实例需要传入。接着看看Choreographer构造方法:

private Choreographer(Looper looper, int vsyncSource) {

mLooper = looper;

//使用当前线程looper创建 mHandler

mHandler = new FrameHandler(looper);

//USE_VSYNC 4.1以上默认是true,表示 具备接受VSync的能力,这个接受能力就是FrameDisplayEventReceiver

mDisplayEventReceiver = USE_VSYNC

? new FrameDisplayEventReceiver(looper, vsyncSource)

: null;

mLastFrameTimeNanos = Long.MIN_VALUE;

// 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms

mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

// 创建一个链表类型CallbackQueue的数组,大小为5,

//也就是数组中有五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)

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

}

代码中都有注释,创建了一个mHandler、VSync事件接收器mDisplayEventReceiver、任务链表数组mCallbackQueues。FrameHandler、FrameDisplayEventReceiver、CallbackQueue后面会一一说明。

4.2.2 安排任务—postCallback

回头看mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)方法,注意到第一个参数是CALLBACK_TRAVERSAL,表示回调任务的类型,共有以下5种类型:

//输入事件,首先执行

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()内部调用postCallbackDelayed(),接着又调用postCallbackDelayedInternal(),来瞅瞅:

private void postCallbackDelayedInternal(int callbackType,

Object action, Object token, long delayMillis) {

...

synchronized (mLock) {

// 当前时间

final long now = SystemClock.uptimeMillis();

// 加上延迟时间

final long dueTime = now + delayMillis;

//取对应类型的CallbackQueue添加任务

mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

if (dueTime <= now) {

//立即执行

scheduleFrameLocked(now);

} else {

//延迟运行,最终也会走到scheduleFrameLocked()

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);

msg.arg1 = callbackType;

msg.setAsynchronous(true);

mHandler.sendMessageAtTime(msg, dueTime);

}

}

}

首先取对应类型的CallbackQueue添加任务,action就是mTraversalRunnable,token是null。CallbackQueue的addCallbackLocked()就是把 dueTime、action、token组装成CallbackRecord后 存入CallbackQueue的下一个节点,具体代码比较简单,不再跟进。

然后注意到如果没有延迟会执行scheduleFrameLocked()方法,有延迟就会使用 mHandler发送MSG_DO_SCHEDULE_CALLBACK消息,并且注意到 使用msg.setAsynchronous(true)把消息设置成异步,这是因为前面设置了同步屏障,只有异步消息才会执行。我们看下mHandler的对这个消息的处理:

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

break;

case MSG_DO_SCHEDULE_VSYNC:

//申请VSYNC信号,例如当前需要绘制任务时

doScheduleVsync();

break;

case MSG_DO_SCHEDULE_CALLBACK:

//需要延迟的任务,最终还是执行上述两个事件

doScheduleCallback(msg.arg1);

break;

}

}

}

直接使用doScheduleCallback方法,看看:

void doScheduleCallback(int callbackType) {

synchronized (mLock) {

if (!mFrameScheduled) {

final long now = SystemClock.uptimeMillis();

if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {

scheduleFrameLocked(now);

}

}

}

}

发现也是走到这里,即延迟运行最终也会走到scheduleFrameLocked(),跟进看看:

private void scheduleFrameLocked(long now) {

if (!mFrameScheduled) {

mFrameScheduled = true;

//开启了VSYNC

if (USE_VSYNC) {

if (DEBUG_FRAMES) {

Log.d(TAG, "Scheduling next frame on vsync.");

}

//当前执行的线程,是否是mLooper所在线程

if (isRunningOnLooperThreadLocked()) {

//申请 VSYNC 信号

scheduleVsyncLocked();

} else {

// 若不在,就用mHandler发送消息到原线程,最后还是调用scheduleVsyncLocked方法

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);

msg.setAsynchronous(true);//异步

mHandler.sendMessageAtFrontOfQueue(msg);

}

} else {

// 如果未开启VSYNC则直接doFrame方法(4.1后默认开启)

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

}

}

}

  1. 如果系统未开启 VSYNC 机制,此时直接发送 MSG_DO_FRAME 消息到 FrameHandler。注意查看上面贴出的 FrameHandler 代码,此时直接执行 doFrame 方法。

  2. Android 4.1 之后系统默认开启 VSYNC,在 Choreographer 的构造方法会创建一个 FrameDisplayEventReceiver,scheduleVsyncLocked 方法将会通过它申请 VSYNC 信号。

  3. isRunningOnLooperThreadLocked 方法,其内部根据 Looper 判断是否在原线程,否则发送消息到 FrameHandler。最终还是会调用 scheduleVsyncLocked 方法申请 VSYNC 信号。

到这里,FrameHandler的作用很明显里了:发送异步消息(因为前面设置了同步屏障)。有延迟的任务发延迟消息、不在原线程的发到原线程、没开启VSYNC的直接走 doFrame 方法取执行绘制。

4.2.3 申请和接受VSync

好了, 接着就看 scheduleVsyncLocked 方法是如何申请 VSYNC 信号的。猜测肯定申请 VSYNC 信号后,信号到来时也是走doFrame() 方法,doFrame()后面再看。先跟进scheduleVsyncLocked():

private void scheduleVsyncLocked() {

mDisplayEventReceiver.scheduleVsync();

}

很简单,调用mDisplayEventReceiver的scheduleVsync()方法,mDisplayEventReceiver是Choreographer构造方法中创建,是FrameDisplayEventReceiver 的实例。 FrameDisplayEventReceiver是 DisplayEventReceiver 的子类,DisplayEventReceiver 是一个 abstract class:

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(this), mMessageQueue,

vsyncSource);

mCloseGuard.open("dispose");

}

在 DisplayEventReceiver 的构造方法会通过 JNI 创建一个 IDisplayEventConnection 的 VSYNC 的监听者。

FrameDisplayEventReceiver的scheduleVsync()就是在 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 {

// 申请VSYNC中断信号,会回调onVsync方法

nativeScheduleVsync(mReceiverPtr);

}

}

那么scheduleVsync()就是使用native方法nativeScheduleVsync()去申请VSYNC信号。这个native方法就看不了了,只需要知道VSYNC信号的接受回调是onVsync(),我们直接看onVsync():

/**

  • 接收到VSync脉冲时 回调

  • @param timestampNanos VSync脉冲的时间戳

  • @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.

  • @param frame 帧号码,自增

*/

@UnsupportedAppUsage

public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {

}

具体实现是在FrameDisplayEventReceiver中:

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

}

@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;

//将本身作为runnable传入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);

}

}

onVsync()中,将接收器本身作为runnable传入异步消息msg,并使用mHandler发送msg,最终执行的就是doFrame()方法了。

注意一点是,onVsync()方法中只是使用mHandler发送消息到MessageQueue中,不一定是立刻执行,如何MessageQueue中前面有较为耗时的操作,那么就要等完成,才会执行本次的doFrame()

4.2.4 doFrame

和上面猜测一样,申请VSync信号接收到后确实是走 doFrame()方法,那么就来看看Choreographer的doFrame():

void doFrame(long frameTimeNanos, int frame) {

final long startNanos;

synchronized (mLock) {

if (!mFrameScheduled) {

return; // no work to do

}

...

// 预期执行时间

long intendedFrameTimeNanos = frameTimeNanos;

startNanos = System.nanoTime();

// 超时时间是否超过一帧的时间(这是因为MessageQueue虽然添加了同步屏障,但是还是有正在执行的同步任务,导致doFrame延迟执行了)

final long jitterNanos = startNanos - frameTimeNanos;

if (jitterNanos >= mFrameIntervalNanos) {

// 计算掉帧数

final long skippedFrames = jitterNanos / mFrameIntervalNanos;

if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {

// 掉帧超过30帧打印Log提示

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;

}

...

mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);

// Frame标志位恢复

mFrameScheduled = false;

// 记录最后一帧时间

mLastFrameTimeNanos = frameTimeNanos;

}

try {

// 按类型顺序 执行任务

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

}

}

上面都有注释了很好理解,接着看任务的具体执行doCallbacks 方法:

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) {

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 {

// 迭代执行队列所有任务

for (CallbackRecord c = callbacks; c != null; c = c.next) {

// 回调CallbackRecord的run,其内部回调Callback的run

c.run(frameTimeNanos);

}

} finally {

synchronized (mLock) {

mCallbacksRunning = false;

do {

final CallbackRecord next = callbacks.next;

//回收CallbackRecord

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

public void run(long frameTimeNanos) {

if (token == FRAME_CALLBACK_TOKEN) {

// 通过postFrameCallback 或 postFrameCallbackDelayed,会执行这里

((FrameCallback)action).doFrame(frameTimeNanos);

} else {

//取出Runnable执行run()

((Runnable)action).run();

}

}

}

前面看到mChoreographer.postCallback传的token是null,所以取出action,就是Runnable,执行run(),这里的action就是 ViewRootImpl 发起的绘制任务mTraversalRunnable了,那么这样整个逻辑就闭环了

那么 啥时候 token == FRAME_CALLBACK_TOKEN 呢?答案是Choreographer的postFrameCallback()方法:

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,并且注意是CALLBACK_ANIMATION类型,

//token是FRAME_CALLBACK_TOKEN,action就是FrameCallback

postCallbackDelayedInternal(CALLBACK_ANIMATION,

callback, FRAME_CALLBACK_TOKEN, delayMillis);

}

public interface FrameCallback {

public void doFrame(long frameTimeNanos);

}

可以看到postFrameCallback()传入的是FrameCallback实例,接口FrameCallback只有一个doFrame()方法。并且也是走到postCallbackDelayedInternal,FrameCallback实例作为action传入,token则是FRAME_CALLBACK_TOKEN,并且任务是CALLBACK_ANIMATION类型。

Choreographer的postFrameCallback()通常用来计算丢帧情况,使用方式如下:

//Application.java

public void onCreate() {

super.onCreate();

//在Application中使用postFrameCallback

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

}

}

4.2.5 小结

使用Choreographer的postCallback()、postFrameCallback() 作用理解:发送任务 存队列中,监听VSync信号,当前VSync到来时 会使用mHandler发送异步message,这个message的Runnable就是队列中的所有任务。

好了,Choreographer整个代码逻辑都讲完了,引用《Android 之 Choreographer 详细分析》的流程图:

image

五、Handler异步消息与同步屏障

=============================================================================

最后来介绍下异步消息与同步屏障。

在Handler中,Message分为3种:同步消息、异步消息、同步屏障消息,他们三者都是Message,只是属性有些区别。

5.1异步消息


通常我们使用创建Handler方式如下:

public Handler() {

this(null, false);

}

注意到内部使用了两个两个参数的构造方法,其中第二个是false:

public Handler(@Nullable Callback callback, boolean async) {

...

mLooper = Looper.myLooper();

if (mLooper == null) {

throw new RuntimeException(

"Can't create handler inside thread " + Thread.currentThread()

  • " that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

mCallback = callback;

//异步标志

mAsynchronous = async;

}

这个false就表示 非异步,即使用的是同步消息,mAsynchronous使用是在enqueueMessage()中:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

//将Handler赋值给Message的target变量

msg.target = this;

//mAsynchronous为false,为同步消息

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

这里如果mAsynchronous是true,就会使用msg.setAsynchronous(true)设置为异步消息。所以上面Choreographer中使用的都是异步消息。

5.2同步屏障消息


postSyncBarrier()方法就是用来插入一个屏障到消息队列的,

//MessageQueue

public int postSyncBarrier() {

return postSyncBarrier(SystemClock.uptimeMillis());

}

private int postSyncBarrier(long when) {

synchronized (this) {

final int token = mNextBarrierToken++;

//注意这里 没有tartget赋值

final Message msg = Message.obtain();

msg.markInUse();

msg.when = when;

msg.arg1 = token;