Choreographer工作原理分析

384 阅读7分钟

基于Android R版本

Choreographer

Choreographer 是一个线程中仅存在一个实例,因此在UI线程只有一个 Choreographer 存在(一个应用中的单例),Choreographer 扮演 Android 渲染链路中承上启下的角色,其作用为:负责控制视图的绘制;

在Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,实现统一调度界面绘图;

  • 负责接收和处理 App 的各种更新消息和回调,等到 Vsync 到来的时候统一处理。比如集中处理 Input(主要是 Input 事件的处理) 、Animation(动画相关)、Traversal(包括 measure、layout、draw 等操作) ,判断卡顿掉帧情况,记录 CallBack 耗时等;
  • Choreographer负责请求(FrameDisplayEventReceiver.scheduleVsync)和接收Vsync信号(FrameDisplayEventReceiver.onVsync);

绘制机制.webp

  • 控制外部输入事件处理,动画执行,UI变化,以及提交执行都是在同一个类中做的处理,即是Choreographer;
  • Choreographer支持5种类型事件:输入、绘制、动画、insets动画、提交,并通过postCallback在对应需要同步vsync进行刷新处进行注册,等待回调;
  • 每次执行的时候,Choreographer会根据当前的时间,只处理事件链表中最后一个事件,当有耗时操作在主线程时,事件不能及时执行,就会出现所谓的“跳帧”,“卡顿”现象;
  • Choreographer的共有方法postCallback(callbackType, Object)是往事件链表中放事件的方法,而doFrame()是消耗这些事件的方法;
  • Choreographer监听底层Vsync信号,一旦接收到回调信号,则通过doFrame统一对java层5种类型事件进行回调;

FrameHandler

private final class FrameHandler extends Handler {
    // 使用Choreographer所在线程的Looper处理消息
    public FrameHandler(Looper looper) {
        // 这里创建的是同步Handler,但是后面所有消息都是异步消息
        // 即后续使用的就是Handler Callback的方式发送消息的,而不是通过sendMessage处理Message
        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的直接走doFrame()方法取执行绘制;

FrameHandler主要在UI线程处理3种类型的消息:

  • MSG_DO_FRAME:开始渲染下一帧的操作;
  • MSG_DO_SCHEDULE_VSYNC:请求Vsync信号;
  • MSG_DO_SCHEDULE_CALLBACK:请求执行callback;

CallbackQueue

public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
​
@UnsupportedAppUsage
private final CallbackQueue[] mCallbackQueues;
​
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];

CallbackQueue.png

在Choreographer的构造方法中,创建了CallbackQueue数组,这个数组用于保存不同类型Callback的队列,对应的个数就是Callback Type size;

Callback 类型

//输入事件,首先执行 
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; 

Choreographer 初始化

我们在执行addView的时候,会创建ViewRootImpl实例,在ViewRootImpl的构造方法中,会获取Choreographer实例:

mChoreographer = useSfChoreographer
                ? Choreographer.getSfInstance() : Choreographer.getInstance();

useSfChoreographer在ViewRootImpl中默认为false,即默认使用getInstance获取Choreographer实例;

public static Choreographer getInstance() {
    return sThreadInstance.get();
}
​
@UnsupportedAppUsage
public static Choreographer getSfInstance() {
    return sSfThreadInstance.get();
}
​
// Thread local storage for the choreographer.
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!");
        }
        Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
        if (looper == Looper.getMainLooper()) {
            mMainInstance = choreographer;
        }
        return choreographer;
    }
};
​
// 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);
    }
};

在Choreographer的初始化过程中,需要传入对应的VSync的信号类型:

  • VSYNC_SOURCE_APP:VSync由硬件发出的硬件信号,应用进程监听VSync信号并通知给Choreographer;
  • VSYNC_SOURCE_SURFACE_FLINGER:由SurfaceFlinger进程的一个线程定时发出的软件信号;
private Choreographer(Looper looper, int vsyncSource) {
    // 调用线程的Looper,所在线程必须有Looper
    mLooper = looper;
    // 使用所在线程的Looper构造一个Handler,处理消息
    mHandler = new FrameHandler(looper);
    // USE_VSYNC默认为true,构造FrameDisplayEventReceiver。注册底层VSYNC回调
    mDisplayEventReceiver = USE_VSYNC
        ? new FrameDisplayEventReceiver(looper, vsyncSource)
        : null;
    mLastFrameTimeNanos = Long.MIN_VALUE;
​
    mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
​
    // 初始化几种类型的CallbackQueue队列
    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));
}

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, CONFIG_CHANGED_EVENT_SUPPRESS);
    }
​
    // 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;
        }
​
        // 保持VSYNC时间等相关信息
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        // 通过obtain()方法获取可重用Message,自身作为Message的callback,即后续会调用FrameDisplayEventReceiver的run()方法
        Message msg = Message.obtain(mHandler, this);
        // 消息设置为异步消息,Choreographer中的所有Messag都是异步消息
        msg.setAsynchronous(true);
        // 向FrameHandler发送消息
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
​
    // 前面发送的消息到Handler,最后执行的是callback中的这个run()方法
    @Override
    public void run() {
        mHavePendingVsync = false;
        // 执行Choreographer核心方法:doFrame()
        doFrame(mTimestampNanos, mFrame);
    }
}

Choreographer 工作流程

我们以StatusBar hide场景为例,分析Choreographer的工作流程;

scheduleVsync

Choreographer.png

执行到scheduleVsync()方法之后,就需要等待VSYNC信号的上报;

doFrame

Choreographer_onVsync.png

在doFrame()方法中,首先先计算帧率相关的一些信息,后续将其信息保存到mFrameInfo对象中进行维护;

最后会依次调用上述描述过的CallbackQueue(Input、Animation、Insets_Animation、Traversal、Commit);

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();
        // 根据Callback Type从mCallbackQueues中取出对应的CallbackQueue,再从中取出CallbackRecord
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
            now / TimeUtils.NANOS_PER_MS);
        // 没有需要执行的CallbackRecord直接返回
        if (callbacks == null) {
            return;
        }
        // 标记为:正在执行Callback
        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.
        // callbackType类型为CALLBACK_COMMIT特殊处理
        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]);
        // 按个执行CallbackRecord
        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);
    }
}

我们以CallbackType = CALLBACK_ANIMATION为例:

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) {
            ((FrameCallback)action).doFrame(frameTimeNanos);
        } else {
            ((Runnable)action).run();
        }
    }
}

从mCallbackQueues[CALLBACK_ANIMATION]中拿到CallbackRecord对象,执行其run()方法;

我们通过追溯CallbackRecord的action的赋值逻辑,可以发现,CallbackRecord中的action的类型为Runnable:

/**
  * <p>Causes the Runnable to execute on the next animation time step.
  * The runnable will be run on the user interface thread.</p>
  *
  * @param action The Runnable that will be executed.
  *
  * @see #postOnAnimationDelayed
  * @see #removeCallbacks
  */
public void postOnAnimation(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        attachInfo.mViewRootImpl.mChoreographer.postCallback(
            Choreographer.CALLBACK_ANIMATION, action, null);
    } else {
        // Postpone the runnable until we know
        // on which thread it needs to run.
        getRunQueue().post(action);
    }
}

post()方法中传入的Runnable类型的参数为mAnimationStarter:

private Runnable mAnimationStarter = new Runnable() {
    @Override
    public void run() {
        startAnimation();
    }
};

addAnimationCallback.png

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        // 如果还有动画回调对象注册,也就是动画没有执行完,会继续请求下一个VSYNC信号,从而稳定的进行动画
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};
​
private void doAnimationFrame(long frameTime) {
    long currentTime = SystemClock.uptimeMillis();
    final int size = mAnimationCallbacks.size();
    for (int i = 0; i < size; i++) {
        final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
        if (callback == null) {
            continue;
        }
        if (isCallbackDue(callback, currentTime)) {
            // 核心逻辑,这一块就执行到了ValueAnimation或者是AnimationSet模块中的doAnimation,这个就需要看使用了哪一种Animation
            callback.doAnimationFrame(frameTime);
            if (mCommitCallbacks.contains(callback)) {
                getProvider().postCommitCallback(new Runnable() {
                    @Override
                    public void run() {
                        commitAnimationFrame(callback, getProvider().getFrameTime());
                    }
                });
            }
        }
    }
    cleanUpList();
}