Android Perfetto 系列 5:Android App 基于 Choreographer 的渲染流程

本文介绍了 App 开发者不经常接触到但在 Android Framework 渲染链路中非常重要的一个类 Choreographer,包括 Choreographer 的引入背景、简介、部分源码解析、与 MessageQueue 的交互、在 APM 中的应用,以及手机厂商基于 Choreographer 的一些优化思路。

Choreographer 的引入主要是配合 Vsync,为上层应用的渲染提供稳定的 Message 处理时机。当 Vsync 信号到来时,系统通过对 Vsync 信号周期的调整,控制每一帧绘制操作的时机。目前主流手机的屏幕刷新率已达到 120Hz,即每 8.3ms 刷新一次,系统为配合屏幕刷新频率,相应调整 Vsync 周期。每个 Vsync 周期到来时,Vsync 信号唤醒 Choreographer 执行应用的绘制操作,这正是引入 Choreographer 的主要作用。了解 Choreographer 还可以帮助应用开发者深入理解每一帧的运行原理,同时加深对 MessageHandlerLooperMessageQueueInputAnimationMeasureLayoutDraw 等核心组件的认识。许多 APM(应用性能监控)工具也利用了 Choreographer(通过 FrameCallback + FrameInfo)、MessageQueue(通过 IdleHandler)和 Looper(通过自定义 MessageLogging)这些组合机制进行性能监测。深入理解这些机制后,开发者可以更有针对性地进行性能优化,形成系统化的优化思路。

本文是 Perfetto 系列文章的第五篇,主要是对 Perfetto 中的 Choreographer 进行简单介绍

本系列的目的是通过 Perfetto 这个工具,从另外一个角度来看待 Android 系统整体的运行,同时也从另外一个角度来对 Framework 进行学习。也许你看了很多讲 Framework 的文章,但是总是记不住代码,或者不清楚其运行的流程,也许从 Perfetto 这个图形化的角度,你可以理解的更深入一些。

Perfetto 系列目录

  1. Android Perfetto 系列目录
  2. Android Perfetto 系列 1:Perfetto 工具简介
  3. Android Perfetto 系列 2:Perfetto Trace 抓取
  4. Android Perfetto 系列 3:熟悉 Perfetto View
  5. Android Perfetto 系列 4:使用命令行在本地打开超大 Trace
  6. Android Perfetto 系列 5:Android App 基于 Choreographer 的渲染流程
  7. Android Perfetto 系列 6:为什么是 120Hz?高刷新率的优势与挑战
  8. Android Perfetto 系列 7 - MainThread 和 RenderThread 解读
  9. Android Perfetto 系列 8:深入理解 Vsync 机制与性能分析
  10. 视频(B站) - Android Perfetto 基础和案例分享

主线程运行机制的本质

在讲 Choreographer 之前,我们先理一下 Android 主线程运行的本质,其实就是 Message 的处理过程,我们的各种操作,包括每一帧的渲染操作 ,都是通过 Message 的形式发给主线程的 MessageQueue ,MessageQueue 处理完消息继续等下一个消息,如下图所示

MethodTrace 图示

Perfetto 图示

演进

引入 Vsync 之前的 Android 版本,渲染一帧相关的 Message ,中间是没有间隔的,上一帧绘制完,下一帧的 Message 紧接着就开始被处理。这样的问题就是,帧率不稳定,可能高也可能低,不稳定,如下图

MethodTrace 图示

Trace 图示(资源比较老,用 Systrace 图示)

可以看到这时候的瓶颈是在 dequeueBuffer, 因为屏幕是有刷新周期的, FB 消耗 Front Buffer 的速度是一定的, 所以 SF 消耗 App Buffer 的速度也是一定的, 所以 App 会卡在 dequeueBuffer 这里,这就会导致 App Buffer 获取不稳定, 很容易就会出现卡顿掉帧的情况.

对于用户来说,稳定的帧率才是好的体验,比如你玩王者荣耀,相比 fps 在 60 和 40 之间频繁变化,用户感觉更好的是稳定在 50 fps 的情况.

所以 Android 的演进中,最初引入了 Vsync + TripleBuffer + Choreographer 的机制,后来在 Android S (Android 12) 版本又进一步引入了 BlastBufferQueue,共同构成了现代 Android 稳定帧率输出机制,让软件层和硬件层可以以共同的频率一起工作。

引入 Choreographer

Choreographer 的引入主要是配合 Vsync,为上层应用的渲染提供稳定的 Message 处理时机。当 Vsync 信号到来时,系统通过对 Vsync 信号周期的调整,控制每一帧绘制操作的时机。目前主流手机的屏幕刷新率已达到 120Hz,即每 8.3ms 刷新一次,系统为配合屏幕刷新频率,相应调整 Vsync 周期。每个 Vsync 周期到来时,Vsync 信号唤醒 Choreographer 执行应用的绘制操作,如果每个 Vsync 周期应用都能渲染完成,那么应用的 fps 就是120,给用户的感觉就是非常流畅,这就是引入 Choreographer 的主要作用

当然目前主流旗舰手机的刷新率已达到120Hz,Vsync周期已缩短至8.3ms,上图中的操作要在更短的时间内完成,对性能的要求也越来越高,具体可以看新的流畅体验,90Hz 漫谈 这篇文章(文章虽然讨论90Hz,但同样的原理适用于120Hz)

Choreographer 简介

Choreographer 扮演 Android 渲染链路中承上启下的角色

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

从上面可以看出,Choreographer 在 Android 渲染管线中扮演关键的协调者角色,其重要性在于通过 Choreographer + SurfaceFlinger + Vsync + BlastBufferQueue 这一套完整的渲染机制,确保 Android 应用能够以稳定的帧率运行(60 fps、90 fps 或 120 fps),有效减少帧率波动带来的视觉不适感。

了解 Choreographer 还可以帮助应用开发者深入理解每一帧的运行原理,同时加深对 MessageHandlerLooperMessageQueueInputAnimationMeasureLayoutDraw 等核心组件的认识。许多 APM(应用性能监控)工具也利用了 Choreographer(通过 FrameCallback + FrameInfo)、MessageQueue(通过 IdleHandler)和 Looper(通过自定义 MessageLogging)这些组合机制进行性能监测。深入理解这些机制后,开发者可以更有针对性地进行性能优化,形成系统化的优化思路。

另外,虽然图表是解释流程的有效方式,但本文将更多依赖 Perfetto 和 MethodTrace 工具的可视化输出。Perfetto 以时间线方式(从左到右)展示整个系统的运行状况,涵盖 CPU、SurfaceFlinger、SystemServer 和应用进程等关键组件的活动。使用 PerfettoMethodTrace 可以直观展示关键执行流程,当您熟悉系统代码后,Perfetto 的输出能够直接映射到设备的实际运行状态。因此,本文除了引用少量网络图表外,主要依靠 Perfetto 来展示分析结果。

从 Perfetto 的角度来看 Choreographer 的工作流程

下图以滑动设置界面为例子,我们先看一下从上到下设置界面的一个完整的预览图,可以看到 Perfetto 中从左到右,每一个绿色的帧都表示一帧,表示最终我们可以手机上看到的画面

  1. 图中每一个 VSYNC-app 的值的区间是一个 Vsync 的时间,对应当前设备的刷新率,如 60Hz 时为 16.6ms,120Hz时为8.3ms,上升沿或者下降沿就是 Vsync 到达的时间
  2. 每一帧处理的流程:接收到 Vsync 信号回调 UI Thread –> RenderThread –> SurfaceFlinger
  3. UI Thread 和 RenderThread 就可以完成 App 一帧的渲染,在最新的 Android 15 中,通过 BlastBufferQueue 机制,渲染完的 Buffer 抛给 SurfaceFlinger 去合成,然后我们就可以在屏幕上看到这一帧了
  4. 可以看到桌面滑动的每一帧耗时都很短(Ui Thread 耗时 + RenderThread 耗时),但是由于 Vsync 的存在,每一帧都会等到 Vsync 才会去做处理

有了上面这个整体的概念,我们将 UI Thread 的每一帧放大来看,看看 Choreographer 的位置以及 Choreographer 是怎么组织每一帧的

Choreographer 的工作流程

  1. Choreographer 初始化
  2. 初始化 FrameHandler,绑定 Looper
  3. 初始化 FrameDisplayEventReceiver,与 SurfaceFlinger 建立通信用于接收和请求 Vsync
  4. 初始化 CallBackQueues
  5. SurfaceFlinger 的 appEventThread 唤醒发送 Vsync,Choreographer 回调 FrameDisplayEventReceiver.onVsync,进入 Choreographer 的主处理函数 doFrame
  6. Choreographer.doFrame 计算掉帧逻辑
  7. Choreographer.doFrame 处理 Choreographer 的第一个 callback:input
  8. Choreographer.doFrame 处理 Choreographer 的第二个 callback:animation
  9. Choreographer.doFrame 处理 Choreographer 的第三个 callback:insets animation
  10. Choreographer.doFrame 处理 Choreographer 的第四个 callback:traversal
  11. traversal-draw 中 UIThread 与 RenderThread 同步数据
  12. Choreographer.doFrame 处理 Choreographer 的第五个 callback:commit
  13. RenderThread 处理绘制命令
  14. 在 Android S (Android 12) 及以上版本中,RenderThread 通过 BlastBufferQueue 向 SurfaceFlinger 提交绘制内容
  15. BlastBufferQueue 由 App 端创建和管理
  16. 通过生产者 (BBQ_BufferQueue_Producer) 和消费者 (BufferQueue_Consumer) 模型工作
  17. UI 线程不必等待 RenderThread 完成,可以更早地准备下一帧,减少了主线程阻塞

详细流程图点击这里

第一步初始化完成后,后续就会在步骤 2-10 之间循环

同时也附上这一帧所对应的 MethodTrace(这里预览一下即可,下面会有详细的大图)

Choreographer 与 RenderThread 及 BlastBufferQueue 的交互

Android S (Android 12) 中,RenderThread 与 SurfaceFlinger 之间的交互发生了重要变化,其核心是 BlastBufferQueue 的引入。下面我们来看看这一机制是如何工作的。

BlastBufferQueue 工作原理

BlastBufferQueue 是一个专为 UI 渲染优化的 BufferQueue 变体,它替代了传统由 SurfaceFlinger 创建的 BufferQueue,转为由 App 端创建和管理,用于 App 与 SurfaceFlinger 之间的缓冲区管理。

  1. 更高效的缓冲区管理 在传统的 BufferQueue 中,App 需要通过 dequeueBuffer 获取一个可用的缓冲区,然后渲染内容,最后通过 queueBuffer 将缓冲区提交给 SurfaceFlinger。这个过程中,如果没有可用的缓冲区,App 需要等待,这会导致阻塞。

    BlastBufferQueue 通过更智能的缓冲区管理,减少了这种等待,特别是在高刷新率设备上,效果更明显。

  2. RenderThread 与 UI 线程的解耦 在 Android S 之前,UI 线程(主线程)需要等待 RenderThread 完成工作才能继续处理下一帧。而在新的架构中,UI 线程可以更早地完成自己的工作,将渲染任务交给 RenderThread 后即可准备下一帧的工作,不必等待当前帧完全渲染完成。

    // 在 ViewRootImpl.performTraversals() 中
    // 旧版本需要等待 draw 完成
    performDraw(); // 这里会阻塞等待 RenderThread
     
    // BlastBufferQueue 引入后
    scheduleTraversals(); // 可以更早地准备下一帧
  3. 创建机制 BlastBufferQueue 在 ViewRootImpl 的 relayoutWindow 过程中创建:

    // 创建 BBQ 的示例代码
    if (mBlastBufferQueue == null) {
        mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
            mSurfaceSize.x, mSurfaceSize.y,
            mWindowAttributes.format);
    }
  4. 与 Choreographer 的配合 Choreographer 仍然是协调这一切的核心。当 Vsync 信号到来时,Choreographer 会触发 doFrame,执行各种回调,其中 CALLBACK_TRAVERSAL 会触发 ViewRootImpl 的 performTraversals(),最终走到 draw 流程。

    在 draw 流程中,通过 BlastBufferQueue,RenderThread 可以更独立地工作,而 UI 线程可以更早地返回处理其他任务。

下面我们就从源码的角度,来看一下具体的实现

源码解析

下面从源码的角度来简单看一下,源码只摘抄了部分重要的逻辑,其他的逻辑则被剔除,另外 Native 部分与 SurfaceFlinger 交互的部分也没有列入,不是本文的重点,有兴趣的可以自己去跟一下。下面的源码基于Android 15 的最新实现。

Choreographer 的初始化

Choreographer 的单例初始化

// Thread local storage for the choreographer.
private static final ThreadLocal<Choreographer> sThreadInstance =
        new ThreadLocal<Choreographer>() {
    @Override
    protected Choreographer initialValue() {
        // 获取当前线程的 Looper
        Looper looper = Looper.myLooper();
        if (looper == null) {
            throw new IllegalStateException("The current thread must have a looper!");
        }
        // 构造 Choreographer 对象,使用 VSYNC_SOURCE_APP 作为Vsync源
        Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
        if (looper == Looper.getMainLooper()) {
            mMainInstance = choreographer;
        }
        return choreographer;
    }
};

Choreographer 的构造函数

private Choreographer(Looper looper, int vsyncSource) {
    mLooper = looper;
    // 1. 初始化 FrameHandler
    mHandler = new FrameHandler(looper);
    // 2. 初始化 DisplayEventReceiver
    mDisplayEventReceiver = USE_VSYNC
            ? new FrameDisplayEventReceiver(looper, vsyncSource)
            : null;
    mLastFrameTimeNanos = Long.MIN_VALUE;
    mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    //3. 初始化 CallbacksQueues
    mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
    for (int i = 0; i <= CALLBACK_LAST; i++) {
        mCallbackQueues[i] = new CallbackQueue();
    }
    ......
}

FrameHandler

private final class FrameHandler extends Handler {
    ......
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_DO_FRAME://开始渲染下一帧的操作
                doFrame(System.nanoTime(), 0);
                break;
            case MSG_DO_SCHEDULE_VSYNC://请求 Vsync 
                doScheduleVsync();
                break;
            case MSG_DO_SCHEDULE_CALLBACK://处理 Callback
                doScheduleCallback(msg.arg1);
                break;
        }
    }
}

Choreographer 初始化链

在 Activity 启动过程,执行完 onResume 后,会调用 Activity.makeVisible(),然后再调用到 addView(), 层层调用会进入如下方法

ActivityThread.handleResumeActivity(IBinder, boolean, boolean, String) (android.app) 
-->WindowManagerImpl.addView(View, LayoutParams) (android.view) 
  -->WindowManagerGlobal.addView(View, LayoutParams, Display, Window) (android.view) 
    -->ViewRootImpl.ViewRootImpl(Context, Display) (android.view) 
    public ViewRootImpl(Context context, Display display) {
        ......
        mChoreographer = Choreographer.getInstance();
        ......
    }

FrameDisplayEventReceiver 简介

Vsync 的注册、申请、接收都是通过 FrameDisplayEventReceiver 这个类,所以可以先简单介绍一下。 FrameDisplayEventReceiver 继承 DisplayEventReceiver , 有三个比较重要的方法

  1. onVsync – Vsync 信号回调
  2. run – 执行 doFrame
  3. scheduleVsync – 请求 Vsync 信号
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
    ......
    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData eventData) {
        ......
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        // Android 15中新增的VsyncEventData传递更丰富的Vsync事件数据
        mVsyncEventData = eventData;
        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, mVsyncEventData);
    }
    
    public void scheduleVsync() {
        ......  
        nativeScheduleVsync(mReceiverPtr);
        ......
    }
}

Choreographer 中 Vsync 的注册

从下面的函数调用栈可以看到,Choreographer 的内部类 FrameDisplayEventReceiver.onVsync 负责接收 Vsync 回调,通知 UIThread 进行数据处理。

那么 FrameDisplayEventReceiver 是通过什么方式在 Vsync 信号到来的时候回调 onVsync 呢?答案是 FrameDisplayEventReceiver 的初始化的时候,最终通过监听文件句柄的形式,其对应的初始化流程如下

android/view/Choreographer.java

private Choreographer(Looper looper, int vsyncSource) {
    mLooper = looper;
    mDisplayEventReceiver = USE_VSYNC
            ? new FrameDisplayEventReceiver(looper, vsyncSource)
            : null;
    ......
}

android/view/Choreographer.java

public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
    super(looper, vsyncSource);
}

android/view/DisplayEventReceiver.java

public DisplayEventReceiver(Looper looper, int vsyncSource) {
    ......
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
            vsyncSource);
}

简单来说,FrameDisplayEventReceiver 的初始化过程中,通过 BitTube (本质是一个 socket pair),来传递和请求 Vsync 事件,当 SurfaceFlinger 收到 Vsync 事件之后,通过 appEventThread 将这个事件通过 BitTube 传给 DisplayEventDispatcher,DisplayEventDispatcher 通过 BitTube 的接收端监听到 Vsync 事件之后,回调 Choreographer.FrameDisplayEventReceiver.onVsync,触发开始一帧的绘制,如下图

DisplayEventReceiver 与 SurfaceFlinger 的通信细节

在 Android 系统中,DisplayEventReceiver 通过 JNI 调用 nativeInit 方法来建立与 SurfaceFlinger 服务的通信通道。这个过程涉及多个关键步骤:

  1. 创建 NativeDisplayEventReceiver 对象:在 Java 层调用 nativeInit 后,JNI 创建一个 NativeDisplayEventReceiver 实例,用于接收 Vsync 信号。
  2. 获取 IDisplayEventConnection:通过 ISurfaceComposer 接口获取 IDisplayEventConnection,这是一个 Binder 接口,用于与 SurfaceFlinger 服务通信。
  3. 建立 BitTube 连接:BitTube 是一个基于 socket pair 的通信机制,专为高频、小数据量的跨进程通信设计。它在 App 进程和 SurfaceFlinger 进程之间创建一个高效的通信通道。[详细参考 Android 中的 BitTube 详解](../../IPC/Android 中的 BitTube 详解.md)
  4. 文件描述符监听:通过 Looper 监听 BitTube 的文件描述符,当有 Vsync 信号到来时,Looper 会通知 DisplayEventDispatcher 处理事件。

整个通信流程如下:

App 进程                                    SurfaceFlinger 进程
  |                                             |
  |-- 创建 DisplayEventReceiver ---------------> |
  |                                             |
  |<- 返回 IDisplayEventConnection (Binder) ----|
  |                                             |
  |-- 创建 BitTube ---------------------------->|
  |                                             |
  |<- 文件描述符交换 -----------------------------|
  |                                             |
  |-- 注册文件描述符到 Looper ---------------------|
  |                                             |
  |-- 请求 Vsync (requestNextVsync) ------------>|
  |                                             |
  |<- 发送 Vsync 事件数据 ------------------------|
  |                                             |
  |-- Looper 通知 -> handleEvent ---------------|
  |                                             |
  |-- 回调 Java onVsync ----------------------|
  |                                             |

这种设计的优势在于避免了使用 Binder 传递高频的 Vsync 事件数据,通过直接的 socket 通信提高了性能和实时性,这对于保证流畅的 UI 渲染至关重要。同时,由于 BitTube 使用了文件描述符,可以无缝集成到 Android 的 Looper 机制中,使得整个系统能够以事件驱动的方式工作。

Choreographer 处理一帧的逻辑

Choreographer 处理绘制的逻辑核心在 Choreographer.doFrame 函数中,从下图可以看到,FrameDisplayEventReceiver.onVsync post 了自己,其 run 方法直接调用了 doFrame 开始一帧的逻辑处理

android/view/Choreographer.java

public void onVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData eventData) {
    ......
    mTimestampNanos = timestampNanos;
    mFrame = frame;
    mVsyncEventData = eventData;
    Message msg = Message.obtain(mHandler, this);
    msg.setAsynchronous(true);
    mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
public void run() {
    mHavePendingVsync = false;
    doFrame(mTimestampNanos, mFrame, mVsyncEventData);
}

doFrame 函数主要做下面几件事

  1. 计算掉帧逻辑
  2. 记录帧绘制信息
  3. 执行 CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_INSETS_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT

记录帧绘制信息

Choreographer 中 FrameInfo 来负责记录帧的绘制信息,doFrame 执行的时候,会把每一个关键节点的绘制时间记录下来,我们使用 dumpsys gfxinfo 就可以看到。当然 Choreographer 只是记录了一部分,剩余的部分在 hwui 那边来记录。

从 FrameInfo 这些标志就可以看出记录的内容,后面我们看 dumpsys gfxinfo 的时候数据就是按照这个来排列的

public @interface FrameInfoFlags {}

    public static final int FRAME_TIMELINE_VSYNC_ID = 1;

    // The intended vsync time, unadjusted by jitter
    public static final int INTENDED_VSYNC = 2;

    // Jitter-adjusted vsync time, this is what was used as input into the
    // animation & drawing system
    public static final int VSYNC = 3;

    // The id of the input event that caused the current frame
    public static final int INPUT_EVENT_ID = 4;

    // When input event handling started
    public static final int HANDLE_INPUT_START = 5;

    // When animation evaluations started
    public static final int ANIMATION_START = 6;

    // When ViewRootImpl#performTraversals() started
    public static final int PERFORM_TRAVERSALS_START = 7;

    // When View:draw() started
    public static final int DRAW_START = 8;

    // When the frame needs to be ready by
    public static final int FRAME_DEADLINE = 9;

    // When frame actually started.
    public static final int FRAME_START_TIME = 10;

    // Interval between two consecutive frames
    public static final int FRAME_INTERVAL = 11;

doFrame 函数记录从 Vsync time 到 markPerformTraversalsStart 的时间

void doFrame(long frameTimeNanos, int frame, VsyncEventData eventData) {
    ......
    mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
    // 处理 CALLBACK_INPUT Callbacks 
    mFrameInfo.markInputHandlingStart();
    // 处理 CALLBACK_ANIMATION Callbacks
    mFrameInfo.markAnimationsStart();
    // 处理 CALLBACK_INSETS_ANIMATION Callbacks
    // 处理 CALLBACK_TRAVERSAL Callbacks
    mFrameInfo.markPerformTraversalsStart();
    // 处理 CALLBACK_COMMIT Callbacks
    ......
}

执行 Callbacks

void doFrame(long frameTimeNanos, int frame, VsyncEventData eventData) {
    ......
    // 处理 CALLBACK_INPUT Callbacks 
    doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    // 处理 CALLBACK_ANIMATION Callbacks
    doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    // 处理 CALLBACK_INSETS_ANIMATION Callbacks
    doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    // 处理 CALLBACK_TRAVERSAL Callbacks
    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    // 处理 CALLBACK_COMMIT Callbacks
    doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
    ......
}

Input 回调调用栈

input callback 一般是执行 ViewRootImpl.ConsumeBatchedInputRunnable

android/view/ViewRootImpl.java

final class ConsumeBatchedInputRunnable implements Runnable {
    @Override
    public void run() {
        doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
    }
}
void doConsumeBatchedInput(long frameTimeNanos) {
    if (mConsumeBatchedInputScheduled) {
        mConsumeBatchedInputScheduled = false;
        if (mInputEventReceiver != null) {
            if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
                    && frameTimeNanos != -1) {
                scheduleConsumeBatchedInput();
            }
        }
        doProcessInputEvents();
    }
}

Input 时间经过处理,最终会传给 DecorView 的 dispatchTouchEvent,这就到了我们熟悉的 Input 事件分发

关于BLASTBuffer 扩展知识

参考了: https://www.cnblogs.com/roger-yu/p/16020275.html