首页
首页
提示词
育儿
Android
首页
提示词
育儿
Android
  • 文章

    • 提示词编写 SOP:从白皮书到可执行流程
    • 降维打击:像研究生一样战斗——初中生「学习黑客」指南
    • Choreographer 深度指南(第一部分):Android Frame Rendering 的心脏
    • Choreographer 中的 Trace 事件 & 函数对应清单
    • Android I/O 优化技术洞察(深水区):从“哪里慢”到“为什么慢”
  • Device Farm 系列

    • USB/IP 完全指南 (1): 原理、命令与实践
    • Android Device Farm 系统设计 (2): MVP 到完整架构
    • Android Device Farm 完整实现 (3): 从代码到上线

Choreographer 中的 Trace 事件 & 函数对应清单

本清单列出 Android Choreographer 中所有的 Trace 事件,以及每个事件对应的函数、函数作用、调用时机、性能影响等信息。


第一层:VSYNC 驱动信号

1.1 onVsync() - VSYNC 信号回调

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ FrameDisplayEventReceiver#onVsync()                      │
│ (在 Perfetto 中显示为蓝色竖线)                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.DisplayEventReceiver.onVsync()             │
│ (FrameDisplayEventReceiver 是 Choreographer 的内部类)  │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 接收来自 SurfaceFlinger 的硬件 VSYNC 信号             │
│ 2. 记录信号到达的精确时间戳:timestampNanos              │
│ 3. 计算本帧的帧时间:frameTimeNanos = timestampNanos     │
│ 4. 发送 Handler 消息:Handler.sendMessage(MSG_DO_FRAME) │
│ 5. UI 线程随后处理消息,调用 Choreographer.doFrame()    │
│                                                          │
│ 伪代码:                                                  │
│ void onVsync(long timestampNanos, long physicalDisplayId)│
│ {                                                         │
│     mTimestampNanos = timestampNanos;                     │
│     // 发送消息给 UI 线程                                 │
│     mHandler.sendMessageAtTime(msg, timestampNanos);     │
│     // msg 会在 UI 线程触发 Choreographer.doFrame()      │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 调用时机                                                  │
├─────────────────────────────────────────────────────────┤
│ • 硬件 VSYNC 信号到达时(60Hz: 每 16.67ms)             │
│ • 120Hz 屏幕:每 8.33ms                                  │
│ • 144Hz 屏幕:每 6.94ms                                  │
│ • **仅在 Mode 1 (USE_VSYNC=true) 时出现**                │
│ • Mode 2 (定时器模式) 中看不到这个事件                   │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 执行耗时:微秒级(<0.01ms)                             │
│ • 间隔精度:±0.1ms(硬件提供)                            │
│ • 关键指标:精确度(60Hz 应该是 16.67ms 的倍数)         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:细细的蓝色竖线                                    │
│ • 位置:UI 线程轨迹上,整个帧处理之前                     │
│ • 间隔:精确均匀(Mode 1)或不均匀(Mode 2)             │
│ • 标签:FrameDisplayEventReceiver::onVsync()             │
│                                                          │
│ 例:                                                      │
│ ├─ [16.67ms] onVsync()                                   │
│ ├─ [33.34ms] onVsync()                                   │
│ ├─ [50.01ms] onVsync()  ← 精确间隔,Mode 1 的表现         │
│ └─ ...                                                   │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: onVsync() 看不到?                                    │
│ A: 可能是 Mode 2 (USE_VSYNC=false),检查系统属性         │
│    adb shell getprop debug.choreographer.vsync           │
│                                                          │
│ Q: onVsync() 间隔不均匀(15ms, 17ms)?                  │
│ A: Mode 2 定时器精度差,或硬件 VSYNC 异常               │
│                                                          │
│ Q: onVsync() 和 doFrame() 之间有延迟?                   │
│ A: UI 线程繁忙,Handler 消息在队列中等待                 │
└─────────────────────────────────────────────────────────┘

第二层:doFrame() - 帧处理核心函数

2.1 doFrame() - 本帧处理开始

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ Choreographer#doFrame()                                  │
│ (在 Perfetto 中显示为绿色/红色 bar)                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.doFrame(long frameTimeNanos) │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 保存当前帧时间                                         │
│    mLastFrameTimeNanos = frameTimeNanos                  │
│                                                          │
│ 2. 依次调用 5 大回调队列(顺序不可变):                  │
│    ├─ doCallbacks(CALLBACK_INPUT, frameTimeNanos)        │
│    ├─ doCallbacks(CALLBACK_ANIMATION, frameTimeNanos)    │
│    ├─ doCallbacks(CALLBACK_INSETS_ANIMATION, ...)        │
│    ├─ doCallbacks(CALLBACK_TRAVERSAL, frameTimeNanos)    │
│    └─ doCallbacks(CALLBACK_COMMIT, frameTimeNanos)       │
│                                                          │
│ 3. 调度下一帧(如果有待执行回调或 VSYNC 已注册)         │
│    scheduleFrame()                                       │
│                                                          │
│ 伪代码:                                                  │
│ void doFrame(long frameTimeNanos) {                      │
│     // 1. 保存帧时间                                     │
│     mLastFrameTimeNanos = frameTimeNanos;                │
│                                                          │
│     // 2. 执行 5 大回调(顺序严格)                       │
│     doCallbacks(CALLBACK_INPUT, frameTimeNanos);         │
│     doCallbacks(CALLBACK_ANIMATION, frameTimeNanos);     │
│     doCallbacks(CALLBACK_INSETS_ANIMATION, ...);         │
│     doCallbacks(CALLBACK_TRAVERSAL, frameTimeNanos);     │
│     doCallbacks(CALLBACK_COMMIT, frameTimeNanos);        │
│                                                          │
│     // 3. 如果有新的回调或 VSYNC 未注册,调度下一帧       │
│     if (mCallbackQueues need scheduling) {              │
│         scheduleFrame();                                 │
│     }                                                    │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 调用时机                                                  │
├─────────────────────────────────────────────────────────┤
│ • 由 onVsync() 发送的 Handler 消息触发                    │
│ • 或由定时器到期触发(Mode 2)                            │
│ • 通常每 16ms 调用一次(60Hz)                           │
│ • 120Hz 屏幕每 8.33ms 调用一次                           │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 耗时预算:16ms(60Hz)/ 8.33ms(120Hz)                │
│ • 关键指标:超时 = Jank(>16ms)                          │
│ • 分布:INPUT(1ms) + ANIM(3ms) + INSETS(1ms) +          │
│         TRAVERSAL(8ms) + COMMIT(2ms) = ~15ms             │
│ • 缓冲:约 1ms 的安全边界                                │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:                                                  │
│   ├─ 绿色 bar:正常(<16ms)                             │
│   ├─ 红色 bar:超时(>16ms),Jank 发生                  │
│   └─ 橙色 bar:接近超时(15-16ms)                       │
│                                                          │
│ • 位置:UI 线程轨迹上,onVsync() 之后                     │
│ • 宽度:帧处理耗时(越窄越好)                            │
│ • 标签:Choreographer::doFrame                           │
│                                                          │
│ 例:                                                      │
│ ├─ [████] doFrame (12ms) ✅ 正常                         │
│ ├─ [████████████] doFrame (18ms) ❌ Jank                │
│ └─ [███] doFrame (8ms) ✅ 很快                           │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: doFrame 是红色(Jank)?                              │
│ A: 找到红色段内部哪个阶段最长,那就是瓶颈                │
│    • TRAVERSAL 长 → View 树太复杂                        │
│    • ANIMATION 长 → 动画计算复杂                         │
│    • INPUT 长 → 输入处理需优化                           │
│                                                          │
│ Q: doFrame 耗时波动很大?                                │
│ A: 可能有动态内容(不同帧的 View 树复杂度不同)          │
│    或内存压力(GC 暂停)                                 │
│                                                          │
│ Q: doFrame 一直是绿色,为什么还是卡顿?                  │
│ A: 可能是后续的 SurfaceFlinger 处理耗时                  │
│    或缓冲区堆积导致的显示延迟                             │
└─────────────────────────────────────────────────────────┘

第三层:5 大回调阶段

3.1 INPUT 回调 - 处理输入事件

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ Choreographer#doFrame -> INPUT                           │
│ (嵌套在 doFrame 内)                                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.doCallbacks(               │
│     int callbackType = CALLBACK_INPUT,                  │
│     long frameTimeNanos                                 │
│ )                                                        │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 从 mCallbackQueues[CALLBACK_INPUT] 取出所有回调       │
│ 2. 遍历执行每一个回调:action.run()                      │
│ 3. 这些 run() 内部处理各种输入事件                        │
│    • 触摸事件(MotionEvent)                             │
│    • 按键事件(KeyEvent)                                │
│    • 焦点变化等                                          │
│                                                          │
│ 伪代码:                                                  │
│ void doCallbacks(int callbackType, long frameTimeNanos) │
│ {                                                         │
│     CallbackRecord[] callbacks =                         │
│         mCallbackQueues[callbackType].mHead;             │
│     while (callbacks != null) {                          │
│         callbacks.run(frameTimeNanos);  // 执行回调       │
│         callbacks = callbacks.next;                      │
│     }                                                    │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 典型回调来源                                              │
├─────────────────────────────────────────────────────────┤
│ • InputManagerService(处理触摸、按键)                   │
│ • View#dispatchTouchEvent()(View 层处理)               │
│ • GestureDetector(手势识别)                            │
│ • InputEventListener                                     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 典型耗时:1ms                                          │
│ • 最大预算:2-3ms(超过则可能导致输入卡顿)              │
│ • 关键指标:应该是 5 大回调中最快的                       │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:doFrame 内的第 1 段小 bar                        │
│ • 位置:5 大回调中最前                                    │
│ • 宽度:通常很小(约 1px)                               │
│ • 标签:Choreographer::doFrame -> INPUT                  │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: INPUT 段很长(>2ms)?                                │
│ A: 输入处理耗时,可能是:                                 │
│    • View#onTouchEvent() 中的复杂逻辑                    │
│    • GestureDetector 计算耗时                            │
│    • 在 INPUT 阶段做了不该做的操作                        │
│                                                          │
│ Q: 触摸操作响应卡顿?                                     │
│ A: 检查前一帧的 TRAVERSAL 是否超时                        │
│    如果前一帧 Jank,这一帧的 INPUT 会被延迟               │
└─────────────────────────────────────────────────────────┘

3.2 ANIMATION 回调 - 更新动画值

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ Choreographer#doFrame -> ANIMATION                       │
│ (嵌套在 doFrame 内)                                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.doCallbacks(               │
│     int callbackType = CALLBACK_ANIMATION,              │
│     long frameTimeNanos                                 │
│ )                                                        │
│                                                          │
│ 最终调用:                                                │
│ android.animation.AnimationHandler.doAnimationFrame()    │
│ android.animation.ValueAnimator.doAnimationFrame()       │
│ android.animation.ObjectAnimator.onAnimationUpdate()     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 从 mCallbackQueues[CALLBACK_ANIMATION] 取回调         │
│ 2. 执行动画回调(通常是 AnimationHandler)               │
│ 3. AnimationHandler.doAnimationFrame() 内部:            │
│    a) 遍历所有正在运行的动画                              │
│    b) 对每个动画调用 animator.doAnimationFrame()         │
│    c) 在 doAnimationFrame() 中:                         │
│       • 计算动画进度:progress = (now - startTime) / dur │
│       • 应用插值器:progress = interpolator.apply(prog)  │
│       • 调用回调:animationListener.onAnimationUpdate()  │
│       • 更新 View 属性:view.setTranslationX()等         │
│                                                          │
│ 伪代码:                                                  │
│ void doAnimationFrame(long frameTimeNanos) {             │
│     for (Animator anim : mRunningAnimators) {            │
│         long elapsedTime = frameTimeNanos - startTime;   │
│         float progress = elapsedTime / duration;         │
│         progress = interpolator.apply(progress);         │
│         anim.onAnimationUpdate(progress); // 更新值      │
│     }                                                    │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 典型回调来源                                              │
├─────────────────────────────────────────────────────────┤
│ • ValueAnimator(属性动画框架)                           │
│ • ObjectAnimator(直接操作 View 属性)                    │
│ • View#postOnAnimation()(View 自定义动画)              │
│ • Animator(过渡动画)                                    │
│ • 用户自定义的 FrameCallback                              │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 典型耗时:2-5ms(取决于动画数量和复杂度)              │
│ • 关键指标:计算进度和应用值的耗时                        │
│ • 最大预算:6-7ms(超过则影响 TRAVERSAL)                │
│ • 关键度量:每个动画的回调耗时(应该 <0.1ms)            │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:doFrame 内的第 2 段 bar                          │
│ • 位置:在 INPUT 之后,TRAVERSAL 之前                     │
│ • 宽度:中等(通常 2-5px)                                │
│ • 标签:Choreographer::doFrame -> ANIMATION              │
│                                                          │
│ 如果看到 ANIMATION 段内有波动:                           │
│ ├─ 可能是因为动画计算导致的帧时间变化                     │
│ └─ 检查是否使用了 System.nanoTime() 而非 frameTime        │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: ANIMATION 段很长(>5ms)?                            │
│ A: 可能原因:                                             │
│    • 运行中的动画太多                                     │
│    • 每个动画的回调都很耗时                               │
│    • 使用了复杂的插值器(计算密集)                       │
│                                                          │
│ Q: 动画不平滑、有 pop/卡顿?                              │
│ A: 最常见的原因是在动画回调中使用了 System.nanoTime()    │
│    改用:Choreographer.getInstance().getFrameTime()     │
│                                                          │
│ Q: ANIMATION 段耗时波动很大?                            │
│ A: 可能是:                                               │
│    • 某些帧的动画计算复杂(如计算路径、alpha混合)        │
│    • 或 GC 暂停导致                                      │
└─────────────────────────────────────────────────────────┘

3.3 INSETS_ANIMATION 回调 - 窗口 Insets 动画

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ Choreographer#doFrame -> INSETS_ANIMATION               │
│ (嵌套在 doFrame 内)                                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.doCallbacks(               │
│     int callbackType = CALLBACK_INSETS_ANIMATION,       │
│     long frameTimeNanos                                 │
│ )                                                        │
│                                                          │
│ 最终调用:                                                │
│ android.view.WindowInsetsController.onFrameUpdate()      │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 更新窗口 Insets 的动画进度                            │
│ 2. Insets 典型场景:                                      │
│    • 输入法(IME)出现/消失                               │
│    • 系统导航栏出现/消失                                  │
│    • 状态栏出现/消失                                      │
│ 3. 按帧更新 Insets 的底部/顶部/左侧/右侧偏移             │
│ 4. 通知 View 树更新布局                                  │
│                                                          │
│ 伪代码:                                                  │
│ void onFrameUpdate(long frameTimeNanos) {                │
│     // 计算 Insets 动画进度                               │
│     float progress = calculateProgress(frameTimeNanos);  │
│     // 更新每个边的 Insets 值                             │
│     int bottom = startInsets + (endInsets - startInsets) │
│                  * progress;                             │
│     // 通知 View 进行 layout 更新                         │
│     dispatchWindowInsetsChangedIfNeeded(bottom);         │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 典型回调来源                                              │
├─────────────────────────────────────────────────────────┤
│ • WindowInsetsController(系统 Insets 控制)             │
│ • InputMethodManager(输入法管理)                       │
│ • WindowInsetsAnimationController                        │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 典型耗时:1ms(通常很快)                              │
│ • 最大预算:2ms                                          │
│ • 关键指标:每帧是否有 Insets 动画在进行                  │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:doFrame 内的第 3 段小 bar                        │
│ • 位置:在 ANIMATION 之后,TRAVERSAL 之前                 │
│ • 宽度:通常很小(约 1px,或看不到)                     │
│ • 标签:Choreographer::doFrame -> INSETS_ANIMATION       │
│                                                          │
│ 注:如果没有 Insets 动画(常见),这段 bar 可能不显示     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: 输入法弹出/消失时卡顿?                                │
│ A: 检查 INSETS_ANIMATION 后的 TRAVERSAL 是否超时         │
│    INSETS 变化会触发新的 layout,可能导致 Jank            │
│                                                          │
│ Q: 看不到 INSETS_ANIMATION 段?                          │
│ A: 正常,说明当前没有 Insets 动画在进行                   │
└─────────────────────────────────────────────────────────┘

3.4 TRAVERSAL 回调 - View 树遍历(最耗时)⭐

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ Choreographer#doFrame -> TRAVERSAL                       │
│ (嵌套在 doFrame 内,最长的段)                          │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.doCallbacks(               │
│     int callbackType = CALLBACK_TRAVERSAL,              │
│     long frameTimeNanos                                 │
│ )                                                        │
│                                                          │
│ → ViewRootImpl.mTraversalRunnable.run()                  │
│ → ViewRootImpl.doTraversal()                             │
│   ├─ performMeasure(child.getMeasuredWidth/Height)      │
│   ├─ performLayout(l, t, r, b)                          │
│   └─ performDraw(canvas)                                │
│                                                          │
│ → View.measure(widthMeasureSpec, heightMeasureSpec)     │
│ → View.layout(left, top, right, bottom)                 │
│ → View.draw(canvas)                                     │
│                                                          │
│ → RenderNode.endAllUpdates()(提交绘制到 GPU)           │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ ** 这是最复杂、最耗时的阶段 **                            │
│                                                          │
│ 1. ViewRootImpl.doTraversal():                          │
│    ├─ 调用 performMeasure()                             │
│    ├─ 调用 performLayout()                              │
│    └─ 调用 performDraw()                                │
│                                                          │
│ 2. performMeasure():                                   │
│    ├─ 从 DecorView(顶级 View)开始                     │
│    ├─ 递归调用每个 View.measure()                        │
│    ├─ 计算每个 View 的宽高(考虑约束和内容)             │
│    └─ 耗时:通常 2-4ms(可能更多如果布局复杂)           │
│                                                          │
│ 3. performLayout():                                    │
│    ├─ 从 DecorView(顶级 View)开始                     │
│    ├─ 递归调用每个 View.layout()                         │
│    ├─ 根据 measure 结果放置 View 的位置                  │
│    └─ 耗时:通常 1-2ms                                  │
│                                                          │
│ 4. performDraw():                                      │
│    ├─ 获取 Canvas(硬件加速或软件渲染)                  │
│    ├─ 递归调用每个 View.draw()                           │
│    ├─ 绘制 View(背景、内容、子 View 等)                │
│    ├─ 对于硬件加速,生成 DisplayList(RenderNode)      │
│    ├─ 提交到 GPU(skia 或 Vulkan)                      │
│    └─ 耗时:通常 4-6ms(可能更多如果有复杂绘制)         │
│                                                          │
│ 伪代码:                                                  │
│ void doTraversal() {                                    │
│     performMeasure(                                     │
│         MeasureSpec.makeMeasureSpec(width, EXACTLY)     │
│     );                                                  │
│     performLayout(                                      │
│         0, 0, width, height                             │
│     );                                                  │
│     performDraw();                                      │
│ }                                                         │
│                                                          │
│ void performMeasure(int widthMeasureSpec, ...) {        │
│     mView.measure(widthMeasureSpec, heightMeasureSpec); │
│     // mView 是 DecorView                               │
│     // 递归下去测量所有子 View                            │
│ }                                                         │
│                                                          │
│ void performLayout(int l, int t, int r, int b) {       │
│     mView.layout(l, t, r, b);                           │
│     // mView 是 DecorView                               │
│     // 递归下去放置所有子 View                            │
│ }                                                         │
│                                                          │
│ void performDraw() {                                    │
│     Surface surface = mSurface;                         │
│     Canvas canvas = surface.lockCanvas();               │
│     mView.draw(canvas);  // 递归绘制                    │
│     surface.unlockCanvasAndPost(canvas);                │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 子事件(嵌套在 TRAVERSAL 内)                            │
├─────────────────────────────────────────────────────────┤
│                                                          │
│ a) performMeasure                                       │
│    对应函数:ViewRootImpl.performMeasure()               │
│    含义:测量所有 View 的宽高                            │
│    耗时:2-4ms 典型                                     │
│    影响:布局层级深、内容复杂会增加耗时                   │
│                                                          │
│ b) performLayout                                        │
│    对应函数:ViewRootImpl.performLayout()                │
│    含义:计算所有 View 的位置坐标                        │
│    耗时:1-2ms 典型                                     │
│    影响:布局计算复杂(权重、约束等)会增加               │
│                                                          │
│ c) performDraw                                          │
│    对应函数:ViewRootImpl.performDraw()                  │
│    含义:绘制所有 View 到 Canvas                         │
│    耗时:4-6ms 典型                                     │
│    影响:绘制操作复杂(阴影、圆角、滤镜等)会增加         │
│                                                          │
│ d) measure (单个 View 级别)                             │
│    对应函数:View.onMeasure()                            │
│    含义:单个 View 计算自己的宽高                        │
│    耗时:<0.1ms(通常很快)                              │
│    深度:可能有数十次调用(树状递归)                     │
│                                                          │
│ e) layout (单个 View 级别)                              │
│    对应函数:View.onLayout()                             │
│    含义:单个 View 放置自己和子 View 的位置              │
│    耗时:<0.1ms(通常很快)                              │
│    深度:可能有数十次调用(树状递归)                     │
│                                                          │
│ f) draw (单个 View 级别)                                │
│    对应函数:View.onDraw()                               │
│    含义:单个 View 绘制自己                              │
│    耗时:0.01-1ms(取决于绘制复杂度)                    │
│    深度:可能有数十次调用(树状递归)                     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 典型耗时分布(以 Activity 为例)                          │
├─────────────────────────────────────────────────────────┤
│ TRAVERSAL 总耗时:~8-10ms(在 16ms 预算中占 50-60%)    │
│   ├─ performMeasure:~30-35% (3-4ms)                    │
│   ├─ performLayout:~10-15% (1-2ms)                     │
│   └─ performDraw:~45-50% (4-6ms)                       │
│                                                          │
│ 如果布局复杂:                                            │
│   ├─ performMeasure:可能达到 6-8ms(深层级布局)        │
│   ├─ performLayout:可能达到 3-4ms(权重计算)           │
│   └─ performDraw:可能达到 8-10ms(复杂绘制)            │
│   → 总计:15-20ms(超过 16ms,Jank)                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 典型耗时:8-10ms                                       │
│ • 最大预算:10-11ms(留空间给其他阶段)                   │
│ • 关键指标:如果 >10ms,下一帧会 Jank                     │
│ • 关键度量:                                              │
│   ├─ View 树深度(应 <10 层)                             │
│   ├─ 单帧 View 总数(应 <100)                            │
│   ├─ 绘制操作复杂度(阴影、滤镜数)                       │
│   └─ Insets 变化频率(会触发额外的 measure/layout)      │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:doFrame 内最长的 bar(占 40-60% 的宽度)         │
│ • 颜色:                                                  │
│   ├─ 绿色:<10ms(正常)                                │
│   ├─ 橙色:10-12ms(接近超时)                          │
│   └─ 红色:>12ms(Jank,严重超时)                      │
│ • 位置:在 INPUT/ANIMATION/INSETS 之后,COMMIT 之前      │
│ • 标签:Choreographer::doFrame -> TRAVERSAL              │
│                                                          │
│ 子 bar:                                                  │
│   ├─ performMeasure bar(约占 30-40%)                  │
│   ├─ performLayout bar(约占 10-15%)                   │
│   └─ performDraw bar(约占 45-50%)                     │
│                                                          │
│ 例:                                                      │
│ doFrame (15ms) ❌                                        │
│   ├─ INPUT (0.5ms)                                      │
│   ├─ ANIMATION (2ms)                                    │
│   ├─ INSETS (0.2ms)                                     │
│   ├─ TRAVERSAL (11ms) ← 红色,超时                      │
│   │  ├─ performMeasure (4ms)                            │
│   │  ├─ performLayout (2ms)                             │
│   │  └─ performDraw (5ms) ← 这部分最耗时                │
│   └─ COMMIT (1.3ms)                                     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断(优化方向)                                  │
├─────────────────────────────────────────────────────────┤
│ Q: TRAVERSAL 是红色(超时)?                            │
│ A: 深入看是哪个子阶段最长:                               │
│    • performMeasure 最长 → 布局层级太深或宽度计算复杂     │
│    • performLayout 最长 → 布局权重计算或约束求解耗时      │
│    • performDraw 最长 → 绘制操作复杂(阴影、圆角等)      │
│                                                          │
│ Q: measure 耗时很长?                                    │
│ A: 原因通常是:                                           │
│    • View 树太深(递归层数多)→ 扁平化布局                │
│    • View 数量太多(100+ Views)→ 减少 View 数量          │
│    • 动态大小计算(如 wrap_content)→ 使用固定大小         │
│    • 权重计算(LinearLayout with weights)→ 减少权重      │
│                                                          │
│ Q: layout 耗时很长?                                     │
│ A: 原因通常是:                                           │
│    • 约束求解复杂 → 使用 ConstraintLayout 的引擎优化      │
│    • Frame 嵌套太多 → 扁平化                             │
│    • 滚动视图嵌套 → 避免多层 ScrollView                   │
│                                                          │
│ Q: draw 耗时很长?                                       │
│ A: 原因通常是:                                           │
│    • 阴影(drop shadow)→ 缓存或预渲染                   │
│    • 圆角(border radius)→ 使用纹理或预裁剪              │
│    • 滤镜效果 → 性能最差,避免实时应用                    │
│    • 自定义绘制(onDraw)→ 优化绘制算法                   │
│    • 图片缩放/旋转 → 预处理到正确尺寸                     │
│                                                          │
│ Q: 打开/关闭某个 View 时 Jank?                          │
│ A: 可能是 Visibility 变化触发的 measure/layout             │
│    一次性显示/隐藏多个 View 时尤其明显                    │
│    解决:使用 setAnimationCacheEnabled() 或分帧处理       │
│                                                          │
│ Q: 列表滚动时 Jank?                                     │
│ A: 通常是 RecyclerView 中 onDraw 太复杂,或:             │
│    • 列表项布局太复杂                                     │
│    • onBindViewHolder 中做了重操作                        │
│    • 图片加载导致的 measure/layout                        │
│                                                          │
│ 优化建议:                                                 │
│ 1. 使用 Layout Inspector 查看 View 树深度                │
│ 2. 在 Perfetto 中启用 "Layout Profiling"                 │
│ 3. 使用 android:debugLayout="true" 查看过度绘制          │
│ 4. 使用 Choreographer.getInstance().getFrameIntervalNanos()│
│ 5. 定期测试:adb shell dumpsys gfxinfo                   │
└─────────────────────────────────────────────────────────┘

3.5 COMMIT 回调 - 缓冲区提交

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ Choreographer#doFrame -> COMMIT                          │
│ (嵌套在 doFrame 内,最后一段)                          │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.doCallbacks(               │
│     int callbackType = CALLBACK_COMMIT,                 │
│     long frameTimeNanos                                 │
│ )                                                        │
│                                                          │
│ → HardwareRenderer.commit()(或 ThreadedRenderer)       │
│ → Surface.unlockCanvasAndPost()                         │
│ → BufferQueue::queueBuffer() (SurfaceFlinger 侧)         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 完成所有绘制操作                                       │
│ 2. 如果是硬件加速:                                       │
│    • 所有 RenderNode 已生成                               │
│    • 提交到 GPU 的 DisplayList                            │
│    • GPU 开始执行渲染(异步)                             │
│                                                          │
│ 3. 如果是软件渲染:                                       │
│    • Canvas 的所有绘制已完成                              │
│    • 缓冲区数据准备好                                     │
│                                                          │
│ 4. 通知 SurfaceFlinger:                                 │
│    • 缓冲区已就绪,可以合成显示                           │
│    • 传递 present time(预期显示时间)                    │
│    • SurfaceFlinger 在下一个 VSYNC 显示这个缓冲区         │
│                                                          │
│ 伪代码:                                                  │
│ void commit() {                                          │
│     if (hardwareAccelerated) {                           │
│         mRenderNode.endRecording();  // 完成 DisplayList  │
│         // GPU 异步渲染                                  │
│     }                                                    │
│     // 通知 SurfaceFlinger 缓冲区就绪                     │
│     mSurface.unlockCanvasAndPost();                      │
│     // 等待 GPU 完成(可能需要 sync)                     │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 典型耗时:1-2ms                                        │
│ • 最大预算:3ms                                          │
│ • 关键指标:如果超过 3ms,可能说明 GPU 繁忙              │
│ • 关键度量:GPU 帧时间(通过 GPU Profiler 查看)         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:doFrame 内的最后一段小 bar                       │
│ • 位置:在 TRAVERSAL 之后,doFrame 结束前                 │
│ • 宽度:通常很小(1-2px)                                │
│ • 标签:Choreographer::doFrame -> COMMIT                 │
│                                                          │
│ 注:COMMIT 阶段的耗时如果长,可能说明:                   │
│   • GPU 队列堆积(GPU 太忙)                              │
│   • 等待 GPU fence(前一帧 GPU 还没完成)                 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: COMMIT 段很长(>2ms)?                               │
│ A: 可能原因:                                             │
│    • GPU 繁忙(上一帧还在渲染)                           │
│    • TRAVERSAL 的绘制操作太复杂                           │
│    • 缓冲区堆积(SurfaceFlinger 还没显示上一帧)          │
│                                                          │
│ Q: GPU profiler 显示 GPU 帧时间很长?                    │
│ A: 检查:                                                 │
│    • 是否有大量的纹理操作                                 │
│    • 是否频繁切换 GPU 着色器                              │
│    • 是否有阴影、滤镜等复杂效果                           │
└─────────────────────────────────────────────────────────┘

第四层:帧调度相关

4.1 scheduleVsyncLocked - 注册下一个 VSYNC

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ scheduleVsyncLocked                                      │
│ (Trace 事件很短,可能看不到)                           │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.scheduleVsyncLocked()        │
│                                                          │
│ 调用链:                                                  │
│ → DisplayEventReceiver.scheduleVsync()                   │
│ → nativeScheduleVsync() (JNI)                            │
│ → FrameDisplayEventReceiver (native 对象)                │
│ → displayEventControl->requestNextVsync() (SurfaceFlinger)│
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 1. 检查是否已注册 VSYNC(避免重复注册)                   │
│ 2. 如果还没有注册,向 SurfaceFlinger 说:                │
│    "我想要下一个 VSYNC 信号"                             │
│ 3. SurfaceFlinger 会在下一个 VSYNC 发生时回调 onVsync()  │
│ 4. 这个调用通常是同步且快速的(JNI 调用)                │
│                                                          │
│ 伪代码:                                                  │
│ void scheduleVsyncLocked() {                             │
│     if (mVsyncScheduled) {                               │
│         return;  // 已经注册,无需重复                    │
│     }                                                    │
│     // 向 SurfaceFlinger 注册 VSYNC 信号请求              │
│     mDisplayEventReceiver.scheduleVsync();               │
│     mVsyncScheduled = true;                              │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 调用时机                                                  │
├─────────────────────────────────────────────────────────┤
│ • doFrame() 即将结束时                                    │
│ • 如果还有待执行的回调或 VSYNC 未注册                     │
│ • Mode 1 (USE_VSYNC=true) 时                             │
│ • 典型频率:每 16ms 左右调用一次                          │
│ • Mode 2 (定时器模式) 不调用此函数                        │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 执行耗时:<0.1ms(很快)                               │
│ • 关键指标:注册是否成功(通过看下一个 onVsync 是否到来) │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:很难看到(执行太快)                              │
│ • 如果看到:会在 doFrame 结束和下一个 VSYNC 之间         │
│ • 通常不是关注的重点(太快)                              │
└─────────────────────────────────────────────────────────┘

4.2 MSG_DO_SCHEDULE_CALLBACK - 延迟回调消息

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ MSG_DO_SCHEDULE_CALLBACK                                 │
│ (Handler 消息处理)                                     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.postCallbackDelayed()        │
│                                                          │
│ 内部处理:                                                │
│ → Handler.sendMessageDelayed(MSG_DO_SCHEDULE_CALLBACK)  │
│ → Handler.handleMessage() 处理消息                       │
│ → Choreographer.scheduleFrameLocked()                   │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 当调用 postCallbackDelayed(type, action, delayMs) 时:   │
│                                                          │
│ 1. 计算 dueTime = System.currentTimeMillis() + delayMs   │
│ 2. 如果 dueTime <= now(延迟已过期):                    │
│    • 立即将回调加入队列                                   │
│    • 调用 scheduleFrameLocked()                          │
│                                                          │
│ 3. 如果 dueTime > now(还需等待):                       │
│    • 计算延迟时间:delay = dueTime - now                 │
│    • 发送 Handler 消息:Handler.postDelayed(...)         │
│    • 经过 delay 毫秒后,消息被处理                        │
│    • MSG_DO_SCHEDULE_CALLBACK 被触发                     │
│    • 回调被加入队列,scheduleFrameLocked() 被调用         │
│                                                          │
│ 伪代码:                                                  │
│ void postCallbackDelayed(int callbackType,              │
│                          Runnable action,               │
│                          long delayMillis) {             │
│     long dueTime = System.currentTimeMillis() + delay;   │
│     if (dueTime <= SystemClock.uptimeMillis()) {         │
│         // 延迟已过期,立即加入                           │
│         addCallbackLocked(callbackType, action);         │
│         scheduleFrameLocked();                           │
│     } else {                                             │
│         // 延迟还未到,发送 Handler 消息                  │
│         Message msg = Message.obtain();                  │
│         msg.what = MSG_DO_SCHEDULE_CALLBACK;             │
│         msg.obj = new CallbackRecord(callbackType, ...); │
│         mHandler.sendMessageAtTime(msg, dueTime);        │
│     }                                                    │
│ }                                                         │
│                                                          │
│ // Handler 在 UI 线程处理消息                             │
│ void handleMessage(Message msg) {                        │
│     if (msg.what == MSG_DO_SCHEDULE_CALLBACK) {          │
│         CallbackRecord record = (CallbackRecord) msg.obj;│
│         addCallbackLocked(record.type, record.action);   │
│         scheduleFrameLocked();  // 调度下一帧             │
│     }                                                    │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 调用时机                                                  │
├─────────────────────────────────────────────────────────┤
│ • 任何时刻调用 postCallbackDelayed() 时                   │
│ • 并且 delay > 0                                         │
│ • MSG_DO_SCHEDULE_CALLBACK 在 delay 毫秒后被处理          │
│ • 例:现在时刻 0,调用 postCallbackDelayed(..., 100ms)  │
│    → MSG_DO_SCHEDULE_CALLBACK 在时刻 100ms 被处理         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 执行耗时:<0.1ms(处理消息本身很快)                    │
│ • 关键指标:delay 是否符合预期                            │
│ • 关键度量:从 postCallbackDelayed 到回调执行的总时间     │
│   = delay + 等待 VSYNC 的时间 (0-16ms)                  │
│   = delay + 0-16ms                                      │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 外观:在 Handler 轨迹中看到的消息处理事件                │
│ • 位置:如果有 100ms 的延迟,会有 100ms 的空白             │
│    然后是 MSG_DO_SCHEDULE_CALLBACK 的消息处理              │
│ • 标签:看不到明确的 "MSG_DO_SCHEDULE_CALLBACK" 标签      │
│         而是看到 Handler 处理消息                          │
│                                                          │
│ 典型场景:                                                │
│ ├─ [0ms] postCallbackDelayed(..., 500ms) 被调用          │
│ ├─ [0-500ms] UI 线程继续处理其他任务                      │
│ ├─ [500ms] MSG_DO_SCHEDULE_CALLBACK 被处理                │
│ ├─ [500-516ms] 等待下一个 VSYNC                          │
│ └─ [516ms] 回调在 VSYNC 时被执行                          │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: 看到长时间的空白(如 1000ms)?                        │
│ A: 这通常是 postCallbackDelayed 的 delay 在等待           │
│    检查:是否有意设置了这么长的延迟?                     │
│    如果是无意的,这可能导致性能问题                       │
│                                                          │
│ Q: MSG_DO_SCHEDULE_CALLBACK 的执行时间比预期晚?          │
│ A: 可能是 UI 线程繁忙,Handler 消息队列有其他任务          │
│    检查:当前 Handler 消息队列是否有堆积                  │
│                                                          │
│ Q: 延迟回调在错误的时刻执行?                             │
│ A: 检查计时是否正确                                       │
│    postCallbackDelayed 的 delay 参数是毫秒                │
│    确保没有单位转换错误                                   │
└─────────────────────────────────────────────────────────┘

4.3 Buffer stuffing recovery - 缓冲区堆积恢复

┌─────────────────────────────────────────────────────────┐
│ Trace 事件                                               │
├─────────────────────────────────────────────────────────┤
│ 没有直接的 Trace 事件名称,但表现为:                     │
│ • 异常的 VSYNC 间隔(变大)                               │
│ • 某些帧的 doFrame 被跳过(没有执行)                     │
│ • "Buffer stuffing" 相关的 Trace 标记(Android 12+)     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 对应函数                                                 │
├─────────────────────────────────────────────────────────┤
│ android.view.Choreographer.onWaitForBufferRelease()     │
│                                                          │
│ 由 SurfaceFlinger 调用,通过 Binder 通信                  │
│ → SurfaceFlinger 检测到缓冲区堆积                         │
│ → 调用 Choreographer 的 onWaitForBufferRelease()         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 函数干什么?                                              │
├─────────────────────────────────────────────────────────┤
│ 场景:app 帧率很高(快速生成缓冲区),但 SurfaceFlinger │
│ 还没来得及显示(缓冲区队列堆积)。                        │
│                                                          │
│ 恢复机制:                                                │
│ 1. SurfaceFlinger 检测到:前几帧的缓冲区还在队列中         │
│ 2. SurfaceFlinger 调用:onWaitForBufferRelease(duration) │
│ 3. Choreographer 收到通知,执行恢复:                     │
│    • 增加帧延迟(frame delay)                            │
│    • 跳过某些 VSYNC 信号                                  │
│    • 暂停 app 的帧生成速度,给 SurfaceFlinger 赶上的机会 │
│ 4. 等待足够的时间后:                                     │
│    • 缓冲区队列被清空                                     │
│    • 恢复正常帧率                                         │
│                                                          │
│ 伪代码:                                                  │
│ void onWaitForBufferRelease(long durationNanos) {        │
│     // durationNanos 是建议的等待时间                     │
│     // 通常是 16ms 或 32ms(跳过 1 或 2 帧)              │
│                                                          │
│     // 增加帧延迟                                         │
│     mFrameDelay = (int) (durationNanos / 1_000_000);     │
│                                                          │
│     // 下一帧会被延迟 mFrameDelay 毫秒                    │
│     // 从而跳过某些 VSYNC 信号                            │
│ }                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 调用时机                                                  │
├─────────────────────────────────────────────────────────┤
│ • SurfaceFlinger 检测到缓冲区堆积时调用                   │
│ • 缓冲区队列长度 >= 2 时通常会触发                        │
│ • Android 10+ 有这个机制                                 │
│ • 生产设备上不常见(app 通常不会这么快)                  │
│ • 可能在高端手机、高帧率 app(90Hz+)上看到              │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 性能指标                                                  │
├─────────────────────────────────────────────────────────┤
│ • 恢复时间:通常 16-32ms(跳过 1-2 帧)                   │
│ • 频率:正常情况下很少发生                                │
│ • 频繁发生说明:app 生成帧的速度超过显示速度              │
│ • 原因:通常是 TRAVERSAL 或 COMMIT 太快                  │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Perfetto 中的表现                                        │
├─────────────────────────────────────────────────────────┤
│ • 异常 1:VSYNC 间隔突然变大(如 16.67ms → 33.34ms)     │
│   说明跳过了一个 VSYNC                                    │
│                                                          │
│ • 异常 2:某一帧的 doFrame 缺失                           │
│   例:时刻 0 → 16.67 → 33.34 → 66.68(跳过 50ms 的帧)  │
│                                                          │
│ • 异常 3:帧时间的规律性缺口                              │
│   如果频繁看到,说明缓冲区经常堆积                         │
│                                                          │
│ 例(缓冲区恢复):                                         │
│ ├─ [0ms] onVsync                                         │
│ ├─ [16.67ms] onVsync                                     │
│ ├─ [33.34ms] onVsync  ← 正常                             │
│ ├─ [50ms] SurfaceFlinger 检测缓冲区堆积                   │
│ │         onWaitForBufferRelease(16ms) 被调用             │
│ ├─ [66.68ms] onVsync  ← 被跳过(被框架延迟 16ms)        │
│ ├─ [83.35ms] onVsync  ← 恢复                             │
│ └─ ...                                                   │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 关键问题诊断                                              │
├─────────────────────────────────────────────────────────┤
│ Q: 经常看到缓冲区堆积恢复?                               │
│ A: 说明 app 帧生成速度过快,而 SurfaceFlinger 跟不上      │
│    可能的原因:                                           │
│    • TRAVERSAL 太快(内容简单)                          │
│    • COMMIT 很快但提交太频繁                              │
│    • SurfaceFlinger 处理过载                             │
│                                                          │
│ Q: 缓冲区堆积导致的掉帧如何避免?                         │
│ A: 通常不需要做什么,这是框架的自适应机制                 │
│    但可以:                                               │
│    • 检查 SurfaceFlinger 的性能(通过 adb)              │
│    • 简化 TRAVERSAL(即使很快也要注意结构)              │
│    • 监测帧时间的一致性                                  │
└─────────────────────────────────────────────────────────┘

总结表:所有 Trace 事件一览

序号Trace 事件对应函数典型耗时Perfetto 表现优先级
1onVsync()DisplayEventReceiver.onVsync()<0.01ms蓝色竖线⭐⭐⭐
2doFrame()Choreographer.doFrame()<16ms绿/红 bar⭐⭐⭐
3INPUTdoCallbacks(CALLBACK_INPUT)~1msdoFrame 第 1 段⭐
4ANIMATIONdoCallbacks(CALLBACK_ANIMATION)~2-5msdoFrame 第 2 段⭐⭐
5INSETS_ANIMATIONdoCallbacks(CALLBACK_INSETS_ANIMATION)~1msdoFrame 第 3 段⭐
6TRAVERSALdoCallbacks(CALLBACK_TRAVERSAL)~8-10msdoFrame 最长段⭐⭐⭐
6.1performMeasureViewRootImpl.performMeasure()~3-4msTRAVERSAL 子事件⭐⭐
6.2performLayoutViewRootImpl.performLayout()~1-2msTRAVERSAL 子事件⭐⭐
6.3performDrawViewRootImpl.performDraw()~4-6msTRAVERSAL 子事件⭐⭐
7COMMITdoCallbacks(CALLBACK_COMMIT)~1-2msdoFrame 最后段⭐⭐
8scheduleVsyncLockedChoreographer.scheduleVsyncLocked()<0.1ms难以看到⭐
9MSG_DO_SCHEDULE_CALLBACKHandler 消息处理变量Handler 轨迹⭐⭐
10Buffer stuffing recoveryonWaitForBufferRelease()16-32msVSYNC 间隔变大⭐⭐

快速参考:问题诊断流程

看到 Trace 中有异常 → 按优先级检查

1️⃣ onVsync() 间隔不均匀?
   → Mode 1 vs Mode 2 问题
   → 检查系统属性

2️⃣ doFrame 是红色(>16ms)?
   → Jank 发生
   → 深入看哪个阶段红色

3️⃣ TRAVERSAL 很长?
   → 查看 performMeasure/Layout/Draw 哪个最长
   → 对症优化(View 树、绘制复杂度等)

4️⃣ ANIMATION 波动?
   → 检查是否使用了 System.nanoTime()
   → 改用 Choreographer.getFrameTime()

5️⃣ VSYNC 间隔变大?
   → 缓冲区堆积恢复机制
   → 检查是否频繁发生(频繁说明有问题)

6️⃣ 长时间空白(1000ms+)?
   → postCallbackDelayed 的 delay 在等待
   → 正常现象,不是问题
Prev
Choreographer 深度指南(第一部分):Android Frame Rendering 的心脏
Next
Android I/O 优化技术洞察(深水区):从“哪里慢”到“为什么慢”