Android 帧绘制耗时全链路溯源

本档旨在从底层 AOSP 源码视角,拆解 JankStats 捕获的每一项耗时指标的物理来源,并精确到具体的类、函数及源码路径。


1. 全链路耗时概览图

一帧的产生经历了:Vsync 信号(Choreographer) -> UI 线程处理(MainThread) -> Sync 同步 -> RenderThread 处理 -> GPU 渲染 -> Buffer 交换

一帧的完整渲染流程

mermaid 代码

sequenceDiagram
    participant C as Choreographer
    participant M as MainThread
    participant R as RenderThread
    participant G as Graphics

    Note over C, M: 1 VSync (INTENDED_VSYNC / VSYNC_TIMESTAMP)
    C->>M: VSync 信号触发
    
    rect rgb(240, 240, 240)
        Note right of M: UNKNOWN_DELAY_DURATION
        M->>M: 2 do other work (Handler/Broadcast)
    end

    rect rgb(230, 255, 230)
        M->>M: 3 handle input (INPUT_HANDLING_DURATION)
        M->>M: 4 play animation (ANIMATION_DURATION)
        M->>M: 5 measure & layout (LAYOUT_MEASURE_DURATION)
        M->>M: 6 draw (DRAW_DURATION)
    end

    M->>R: 7 sync and upload (SYNC_DURATION)
    
    rect rgb(230, 230, 255)
        R->>G: 8 dequeue buffer (阻塞点)
        R->>R: 9 execute
        R->>G: 10 flush command (COMMAND_ISSUE_DURATION)
        R->>G: 11 swap buffer (SWAP_BUFFERS_DURATION)
    end

    G->>G: 12 compose (系统合成)

    Note over G: GPU_DURATION (GPU 异步执行耗时)
    Note over C, G: TOTAL_DURATION (1-11 阶段总和)

流程详细说明与 FrameMetrics 参数对应

流程编号 流程名称 详细说明 对应 FrameMetrics 参数
1 VSync Choreographer 接收到硬件 VSync 信号,触发主线程。 INTENDED_VSYNC_TIMESTAMP (预期) / VSYNC_TIMESTAMP (实际)
2 do other work 主线程在处理 UI 前执行的其他任务(如 Handler 消息、广播)。 UNKNOWN_DELAY_DURATION
3 handle input 处理触摸、按键等输入事件。 INPUT_HANDLING_DURATION
4 play animation 执行属性动画、帧动画等。 ANIMATION_DURATION
5 measure & layout 对 View 树进行测量和布局,确定每个控件的大小和位置。 LAYOUT_MEASURE_DURATION
6 draw 录制绘制指令 (DisplayList),将 Canvas 绘制转化为 Native 指令。 DRAW_DURATION
7 sync and upload 将主线程录制的绘制信息和资源(如 Bitmap)同步到渲染线程。 SYNC_DURATION
8 dequeue buffer 渲染线程从 BufferQueue 申请缓冲区。此处是著名的阻塞点。 (包含在 TOTAL_DURATION 中,或由 DequeueBufferDuration 统计)
9-10 execute & flush 渲染线程回放绘制指令,调用 GPU 渲染并刷新指令队列。 COMMAND_ISSUE_DURATION
11 swap buffer 将渲染完成的 Buffer 提交给 SurfaceFlinger SWAP_BUFFERS_DURATION
12 compose SurfaceFlinger 进行画面合成并显示到屏幕。 (属于系统层,不计入应用内耗时)

FrameMetrics 详细指标参考表

FrameMetrics 的常量值 含义 可能导致耗时多久的情况
UNKNOWN_DELAY_DURATION UI 线程延迟处理绘制任务的耗时 主线程消息队列中正在执行的非绘制任务耗时太久,导致开始执行绘制任务太晚
INPUT_HANDLING_DURATION 输入事件处理函数的耗时 点击事件耗时太久
ANIMATION_DURATION 动画回调函数的耗时 动画太多或者回调函数耗时太久
LAYOUT_MEASURE_DURATION 整个 View 树的测量和布局的耗时 布局层级复杂,频繁改变尺寸或位置(比如复杂的属性动画)
DRAW_DURATION 布局绘制函数的耗时 draw/onDraw 里执行了耗时操作
SYNC_DURATION 主线程同步 DisplayList 到 RenderThread 的耗时 过度绘制,导致要更新的 DisplayList 过多
COMMAND_ISSUE_DURATION RenderThread 发送绘制命令到 GPU 的耗时 绘制内容复杂
SWAP_BUFFERS_DURATION 将绘制的 buffer 交换到前台显示的耗时
TOTAL_DURATION 绘制一帧的总耗时 前面任意一个阶段耗时,最终这个值都会变大
FIRST_DRAW_FRAME 这帧是否是当前窗口的第一帧
INTENDED_VSYNC_TIMESTAMP 预期接收到 VSync 信号(也就是这帧开始执行)的时间戳
VSYNC_TIMESTAMP 真正接收到 VSync 信号的时间 主线程的耗时任务太多,会导致主线程响应 VSync 信号过慢
GPU_DURATION GPU 完成这帧绘制的耗时

其他核心参数解析

  • TOTAL_DURATION: 从预期 VSync 信号发出到帧绘制完成(Swap Buffer 结束)的总时间。即 1-11 阶段的总和。
  • GPU_DURATION: GPU 执行渲染指令的实际耗时(从指令提交完成到 GPU 硬件执行完毕)。反映了 GPU 的真实压力。
  • DEADLINE: 系统为该帧设定的截止时间。如果 TOTAL_DURATION > DEADLINE,通常意味着发生了卡顿。
  • FIRST_DRAW_FRAME: 标记该帧是否为窗口可见后的第一帧。由于需要初始化资源,首帧耗时通常较高。

2. 第一阶段:起跑线前的等待 (Schedule & Input)

核心指标:UNKNOWN_DELAY_DURATION(未知延迟时长)

  • 物理意义:从系统产生 Vsync 信号到 UI 线程真正开始执行 doFrame 任务的时间差。
  • 精确路径
    1. 起点DisplayEventReceiver 收到底层信号,通过 JNI 回调发送消息到 Looper。
      • 源码:frameworks/base/core/java/android/view/DisplayEventReceiver.java
    2. 关键函数Choreographer$FrameDisplayEventReceiver.run() 被 Looper 唤醒,调用 doFrame
      • 源码:frameworks/base/core/java/android/view/Choreographer.java
    3. 终点Choreographer.doFrame() 开始执行。
  • 计算逻辑
    UNKNOWN_DELAY = FrameInfo[HANDLE_INPUT_START] - FrameInfo[INTENDED_VSYNC]
  • 解析
    • INTENDED_VSYNC:理想的帧开始时间戳(Vsync 信号发生的时刻)。
    • HANDLE_INPUT_START:UI 线程 Looper 实际分发到该渲染消息并开始处理的时刻。
  • 参数价值:反映 UI 线程的消息队列积压情况。如果此值大,说明 UI 线程正在执行非 UI 的耗时任务(如长耗时的 Handler 消息或过重的业务逻辑)。

3. 第二阶段:UI 线程的奔跑 (Logic & Construction)

核心指标:INPUT_HANDLING(输入处理) + ANIMATION(动画) + LAYOUT_MEASURE(布局测量) + DRAW(绘制)

  • 物理意义:UI 线程执行业务逻辑并构建绘制指令的过程。
  • 精确路径
    1. Input HandlingChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT) -> InputEventReceiver.consumeEvents()
      • 源码:frameworks/base/core/java/android/view/InputEventReceiver.java
    2. AnimationsChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION) -> 执行所有已注册 of the ValueAnimator 等回调。
    3. Layout & MeasureChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL) -> ViewRootImpl.performTraversals()
      • performTraversals 核心步骤解析
        • relayoutWindow:通过 IPC 与 WindowManagerService 通信,获取或调整 Surface,确定窗口的物理大小(Frame Size)。
        • **performMeasure()**:从顶层 DecorView 开始深度优先遍历 View 树。每个 View 根据父容器的 MeasureSpec 计算自己的 MeasuredWidthMeasuredHeight
        • **performLayout()**:根据测量结果确定 View 在窗口中的最终位置坐标(Left, Top, Right, Bottom),并触发 View 的 onLayout()
        • **performDraw()**:启动绘制阶段,将 View 树的视觉信息转化为渲染指令。
      • 源码:frameworks/base/core/java/android/view/ViewRootImpl.java
    4. **Draw (Recording)**:ViewRootImpl.performDraw() -> ThreadedRenderer.draw() -> View.updateDisplayListIfDirty()
      • 源码:frameworks/base/core/java/android/view/ThreadedRenderer.java
      • 注意:此处是在录制指令到 DisplayList,而非真正渲染。
  • 参数价值:反映 布局复杂度 (Over-nesting)主线程计算压力

4. 第三阶段:交接棒 (Handoff / Sync)

核心指标:SYNC_DURATION(同步时长)

  • 物理意义:UI 线程阻塞等待,将绘制指令和 Bitmap 资源同步给 RenderThread。
  • 精确路径
    1. Java 端入口ThreadedRenderer.nSyncAndDrawFrame() (JNI 调用)。
      • 源码:frameworks/base/core/java/android/view/ThreadedRenderer.java
    2. Native 端核心CanvasContext::prepareTree()
      • 源码:frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
    3. 资源上传:在此阶段,新创建的 Bitmap 会被上传到 GPU 纹理缓存。
  • 计算逻辑:记录从 UI 线程发起同步请求到 RenderThread 成功接收并完成资源拷贝,且 UI 线程被唤醒的时间。
  • 参数价值Bitmap 优化的核心指标。如果 SYNC 耗时高,通常是由于该帧上传了过大的 Bitmap 或触发了大量的资源回收与重新分配。

5. 第四阶段:渲染线程的冲刺 (Recording & Issue)

核心指标:COMMAND_ISSUE_DURATION(指令发布时长)

  • 物理意义:RenderThread 将 DisplayList 转换为真正的 GPU 指令(OpenGL/Vulkan)并提交给驱动。
  • 精确路径
    1. RenderThread 循环RenderThread::threadLoop()
      • 源码:frameworks/base/libs/hwui/renderthread/RenderThread.cpp
    2. 核心执行CanvasContext::draw() 调用渲染管线。
    3. 渲染管线SkiaPipeline::renderFrame() (Android 8.0+ 默认使用 Skia)。
      • 源码:frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp
  • 参数价值:反映 绘制指令的复杂程度。DrawCalls 越多、Path 路径越复杂、或者使用了复杂的 Shader,此项耗时越高。

6. 第五阶段:终点线 (GPU & Swap)

核心指标:SWAP_BUFFERS_DURATION(缓冲区交换时长)

  • 物理意义:RenderThread 调用 eglSwapBuffers 后,等待 GPU 渲染完成并交换缓冲区的阻塞时间。
  • 精确路径
    1. 核心函数CanvasContext::swapBuffers()
    2. 底层调用EglManager::swapBuffers() -> eglSwapBuffers()
      • 源码:frameworks/base/libs/hwui/renderthread/EglManager.cpp
    3. 系统反馈:底层 SurfaceFlinger 消费 Buffer 的速度(涉及 BufferQueue)。
  • 解析:如果 GPU 还没算完,或者系统显示队列已满(存在背压),此调用会阻塞 RenderThread,直到有可用的 Buffer。
  • 面试价值:反映 GPU 渲染压力 (Overdraw)系统三重缓冲 (Triple Buffering) 的饱和度。也是衡量 SurfaceFlinger 消费压力的关键点。

7. 第六阶段:最终审判 (Judgment)

核心指标:TOTAL_DURATION(总耗时) & frameOverrunNanos(帧超时时间) (API 31+)

  • 物理意义:一帧从计划开始到最终出屏全链路总耗时及其与截止日期的偏离量。
  • 精确路径
    • Java 层android.view.FrameMetrics
      • 源码:frameworks/base/core/java/android/view/FrameMetrics.java
    • Native 层google::hwui::FrameInfo
      • 源码:frameworks/base/libs/hwui/FrameInfo.cpp
  • 卡顿判定准则
    1. 经典准则TOTAL_DURATION > 16.6ms (针对 60Hz)。
    2. **JankStats 准则 (API 31+)**:frameOverrunNanos > 0。即该帧物理上错过了 VSync 的 Deadline(最准确的掉帧判定)。

Q&A

  • Q:如果 UI 线程只用了 2ms,为什么还是掉帧了?
    • A:通过 FrameMetrics 分析,可能是 UNKNOWN_DELAY 极大(消息队列排队严重)或 SWAP_BUFFERS 阻塞严重(GPU 瓶颈或渲染队列满了)。
  • Q:SYNC_DURATION(同步时长)过高怎么排查?
    • A:重点检查该帧是否有大图加载、ImageView 设置了巨大的 Bitmap,或者是否在 onDraw 之外频繁触发了硬件资源更新。
  • Q:为什么硬件加速下,RenderThread 卡顿会影响 UI 线程?
    • A:因为 SYNC 阶段是同步的。UI 线程必须在 CanvasContext::prepareTree 期间等待 RenderThread 接收数据,只有等 RenderThread “接手”成功后,UI 线程才能继续处理下一个 Vsync。

Android 帧绘制耗时全链路源码溯源

本文档详细记录了从 Java 层到 Native 层的 Android 帧刷新全链路核心函数(Android 15),并对源码关键逻辑进行了中文注释。


一、 Java 层:UI 线程任务调度 (frameworks/base/core/java/android/view/)

1. 帧回调起点:Choreographer.doFrame()

这是主线程响应 VSYNC 信号的总入口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
boolean resynced = false;
try {
// 1. 更新当前帧的时间线数据(VsyncId、预期呈现时间、截止时间)
FrameTimeline timeline = mFrameData.update(frameTimeNanos, vsyncEventData);

if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + timeline.mVsyncId);
}

synchronized (mLock) {
if (!mFrameScheduled) {
return; // 如果当前没有安排帧,说明是过期的信号,直接返回
}

// 2. 抖动计算 (Jitter):判断主线程是否存在耗时操作导致响应延迟
long intendedFrameTimeNanos = frameTimeNanos; // 信号发出的预期时间
startNanos = System.nanoTime(); // 实际开始处理的时间
final long jitterNanos = startNanos - frameTimeNanos;

if (jitterNanos >= frameIntervalNanos) {
// 如果延迟超过一个周期,说明主线程发生了卡顿
long lastFrameOffset = jitterNanos % frameIntervalNanos;
// 修正 frameTime 为最近的一个模拟 VSYNC 点,保证动画平滑
frameTimeNanos = startNanos - lastFrameOffset;
final long skippedFrames = jitterNanos / frameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! 应用程序可能在主线程做了太多工作。");
}
// 掉帧后重新同步时间线
timeline = mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos);
resynced = true;
}

// 3. 记录帧统计信息,供后续 JankTracker 统计卡顿
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos,
vsyncEventData.preferredFrameTimeline().vsyncId,
vsyncEventData.preferredFrameTimeline().deadline, startNanos,
vsyncEventData.frameInterval);
mFrameScheduled = false; // 重置安排标记
mLastFrameTimeNanos = frameTimeNanos;
}

// 4. 锁定动画时钟,确保本帧内所有动画基于相同的时间基准
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS,
timeline.mExpectedPresentationTimeNanos);

// 5. 调用 doCallbacks 依次按顺序执行回调:输入 -> 动画 -> 缩进动画 -> 遍历(Layout/Draw) -> 提交
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameIntervalNanos);

mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameIntervalNanos);

mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameIntervalNanos);

doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

补充 VsyncEventData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#pragma once

#include <android/gui/FrameTimelineInfo.h>

#include <array>

namespace android::gui {

/**
* VsyncEventData 是一个简单的 POD(Plain Old Data)结构,用于存储 Vsync 相关数据。
* 例如,它可以方便地在 DisplayEventReceiver::Event 的联合体(union)中使用。
*/
struct VsyncEventData {
// 帧时间轴的最大容量,设置为 7 是为了在灵活性和内存开销之间取得平衡。
static constexpr int64_t kFrameTimelinesCapacity = 7;

// 当前帧被调度时的帧间隔时间(纳秒)。反映了当前的屏幕刷新率。
int64_t frameInterval;

// 系统推荐的首选帧时间轴(frameTimelines 数组中的索引)。
uint32_t preferredFrameTimelineIndex;

// 平台当前提供的有效帧时间轴数量;最大值为 kFrameTimelinesCapacity。
uint32_t frameTimelinesLength;

/**
* FrameTimeline 定义了一个特定的渲染预期。
* 现代 Android 系统提供多个候选时间轴,允许应用根据其渲染能力选择最合适的呈现时刻。
*/
struct alignas(8) FrameTimeline {
// 与此 Vsync 事件对应的唯一 ID。
// 此 ID 将用于填充 ISurfaceComposer::setFrameTimelineVsync 等接口,
// 以便系统能够追踪特定帧的流水线状态。
int64_t vsyncId;

// 应用必须在该时刻之前完成其帧渲染任务(包括 CPU 和 GPU 上的所有工作)。
// 使用 CLOCK_MONOTONIC 单调时钟,单位为纳秒。
int64_t deadlineTimestamp;

// 预期该帧画面在物理屏幕上真正显示的时刻(纳秒)。
int64_t expectedPresentationTime;
} frameTimelines[kFrameTimelinesCapacity]; // 按时间顺序排序的候选帧时间轴。

// 获取首选帧时间轴的 Vsync ID。
int64_t preferredVsyncId() const;

// 获取首选帧时间轴的截止时间戳(Deadline)。
int64_t preferredDeadlineTimestamp() const;

// 获取首选帧时间轴的预期呈现时间戳。
int64_t preferredExpectedPresentationTime() const;
};

/**
* ParcelableVsyncEventData 是 VsyncEventData 的包装类,
* 继承自 Parcelable,使其能够通过 Binder 在进程间进行序列化传输。
*/
struct ParcelableVsyncEventData : public Parcelable {
VsyncEventData vsync;

status_t readFromParcel(const Parcel*) override;
status_t writeToParcel(Parcel*) const override;
};
} // namespace android::gui

接过来自底层 DisplayEventReceiver.cpp 传上来的“发令枪响时间” (frameTimeNanos)。
记录:把主线程此时此刻的“起跑时间” (startNanos) 记录下来。
UNKNOWN_DELAY_DURATION = startNanos - frameTimeNanos

2. 任务分发:Choreographer.doCallbacks()

这是 Choreographer 内部用于依次执行各阶段任务(输入、动画、遍历、提交)的核心逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* 执行指定类型的回调任务。
*
* @param callbackType 回调类型(INPUT, ANIMATION, INSETS_ANIMATION, TRAVERSAL, COMMIT)
* @param frameIntervalNanos 当前设备的帧间隔(例如 60Hz 约为 16.6ms)
*/
void doCallbacks(int callbackType, long frameIntervalNanos) {
CallbackRecord callbacks;
long frameTimeNanos = mFrameData.mFrameTimeNanos;

synchronized (mLock) {
final long now = System.nanoTime();

// 提取所有“到期”的回调
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);

if (callbacks == null) {
return;
}
mCallbacksRunning = true;

// 特殊处理:提交阶段 (CALLBACK_COMMIT),修正帧时间基准以应对严重掉帧
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
if (frameIntervalNanos > 0 && jitterNanos >= 2 * frameIntervalNanos) {
final long lastFrameOffset = jitterNanos % frameIntervalNanos + frameIntervalNanos;
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos);
}
}
}

try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
// 执行回调。对于 CALLBACK_TRAVERSAL 类型,会进入 ViewRootImpl.performTraversals()
c.run(mFrameData);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

这在 Choreographer 的底层逻辑中是一个非常重要的保护机制,用于解决因严重的掉帧或主线程卡顿导致的 “逻辑时间偏差”


1. 核心意图:为什么要进行这一步计算?

在 Android 的动画(Animation)和图形渲染中,所有计算都是基于 frameTimeNanos 这个基准的。计算逻辑通常如下:

$$\text{progress} = \frac{\text{currentTime} - \text{startTime}}{\text{duration}}$$

如果系统发生了严重的卡顿(例如主线程阻塞了 500ms),frameTimeNanos 依然停留在 500ms 之前。如果不进行校正,当代码恢复执行时,程序会使用这个“过时”的 frameTimeNanos。这会导致:

  • 动画“跳变”:动画会瞬间计算出很大的 delta,导致元素直接从起点“瞬移”到终点,产生视觉上的闪烁。
  • 物理引擎异常:如果你的代码里有基于物理模拟的逻辑,巨大的时间差会导致计算出的速度、位移爆炸。

2. 代码逻辑拆解

这段代码在 CALLBACK_COMMIT 阶段触发,其逻辑是在检测到极度严重掉帧时,强行将“当前的逻辑帧时间”重置到最近的一个有效时间点。

  • **jitterNanos (抖动/延迟量)**:

    $$jitterNanos = now - frameTimeNanos$$

    这是当前物理时间(now)距离该帧预期执行时间(frameTimeNanos)的偏移量。如果该值很大,说明我们在这一帧上滞后了很多。

  • **阈值判断 (jitterNanos >= 2 \* frameIntervalNanos)**:

    这是为了避免误判。只有当延迟超过 2 个帧周期(在 60Hz 下约 > 33.3ms)时,才会介入。这说明系统已经错过了至少一个 VSYNC 信号,进入了严重的掉帧状态。

  • **重新对齐 frameTimeNanos**:

    Java

    1
    2
    final long lastFrameOffset = jitterNanos % frameIntervalNanos + frameIntervalNanos;
    frameTimeNanos = now - lastFrameOffset;

    这里将 frameTimeNanos 设置为 now 减去一个偏移量。这个偏移量被强制限制在 frameIntervalNanos2 * frameIntervalNanos 之间。

    效果:它将 frameTimeNanos “强制拉回”到距离当前 now 最近的、符合渲染周期节奏的时间点。


3. 可视化理解

上图展示了当发生卡顿时,虚拟时间轴如何被校准以平滑后续动画。

状态 逻辑时间点 结果
正常情况 frameTimeNanos 是旧的信号时间 动画平滑,delta 很小。
严重卡顿 frameTimeNanos 滞后 > 33ms 如果不修正,动画下一帧会因为 currentTime - frameTimeNanos 过大而发生“ teleport” (瞬移)。
修正后 frameTimeNanos 被重置到 now - interval 动画认为上一帧是 16.6ms 前,从而继续平滑过渡,掩盖了卡顿期间的时间断层。

3. 核心负载:Choreographer.FrameData

封装了 VSYNC 信号的时间戳和多组呈现时间线(Frame Timeline),这是现代 Android 系统平滑显示的核心数据结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* {@link VsyncCallback} 的负载,包括帧信息(例如开始渲染帧的时间)
* 以及多个可能的帧时间轴及其信息(包括截止时间和预期呈现时间)。
*/
public static class FrameData {
// 当前帧的时间戳(纳秒),用于动画计算的基准
private long mFrameTimeNanos;
// 包含多个候选的帧时间轴。每个时间轴对应不同的预期呈现时间
private FrameTimeline[] mFrameTimelines;
// 系统推荐的时间轴索引。如果应用无法在 16.6ms 内完成,可能会调整此索引
private int mPreferredFrameTimelineIndex;
// 标记当前是否处于 vsync 回调处理流程中,防止非法访问
private boolean mInCallback = false;

FrameData() {
// 初始化时间轴数组,容量通常由 DisplayEventReceiver 决定
allocateFrameTimelines(DisplayEventReceiver.VsyncEventData.FRAME_TIMELINES_CAPACITY);
}

/** 开始渲染帧的时间(以纳秒为单位)。 */
public long getFrameTimeNanos() {
checkInCallback();
return mFrameTimeNanos;
}

/** 可能的帧时间轴,按时间顺序排序。包含不同刷新率下的预测数据。 */
@NonNull
@SuppressLint("ArrayReturn") // 为了 API 一致性和速度。
public FrameTimeline[] getFrameTimelines() {
checkInCallback();
return mFrameTimelines;
}

/** 平台首选的帧时间轴。应用应优先尝试达到此时间轴定义的 deadline。 */
@NonNull
public FrameTimeline getPreferredFrameTimeline() {
checkInCallback();
return mFrameTimelines[mPreferredFrameTimelineIndex];
}

/** 设置回调状态标记 */
void setInCallback(boolean inCallback) {
mInCallback = inCallback;
for (int i = 0; i < mFrameTimelines.length; i++) {
mFrameTimelines[i].setInCallback(inCallback);
}
}

/** 安全检查,确保 FrameData 仅在 VSYNC 回调期间被有效读取 */
private void checkInCallback() {
if (!mInCallback) {
throw new IllegalStateException(
"FrameData 在 vsync 回调之外无效");
}
}

/** 分配时间轴存储空间 */
private void allocateFrameTimelines(int length) {
// 维护一个默认帧时间轴以保持 API(如 getFrameTimelines 和 getPreferredFrameTimeline)的一致性。
length = Math.max(1, length);

if (mFrameTimelines == null || mFrameTimelines.length != length) {
mFrameTimelines = new FrameTimeline[length];
for (int i = 0; i < mFrameTimelines.length; i++) {
mFrameTimelines[i] = new FrameTimeline();
}
}
}

/**
* 正常更新:使用从底层 DisplayEventReceiver 接收的 Vsync 数据同步更新。
*/
FrameTimeline update(
long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
allocateFrameTimelines(vsyncEventData.frameTimelinesLength);
mFrameTimeNanos = frameTimeNanos;
mPreferredFrameTimelineIndex = vsyncEventData.preferredFrameTimelineIndex;
for (int i = 0; i < mFrameTimelines.length; i++) {
DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline =
vsyncEventData.frameTimelines[i];
mFrameTimelines[i].update(frameTimeline.vsyncId,
frameTimeline.expectedPresentationTime, frameTimeline.deadline);
}
return mFrameTimelines[mPreferredFrameTimelineIndex];
}

/**
* 掉帧后补偿更新:当主线程延迟启动(jitterNanos > frameInterval)时,重新寻找一个有效的时间轴。
*
* @param frameTimeNanos 修正后的帧时间
* @param displayEventReceiver 用于在本地缓存失效时向系统索要最新 Vsync 数据
* @param jitterNanos 实际开始时间与预定时间的偏差(抖动量)
*/
FrameTimeline update(
long frameTimeNanos, DisplayEventReceiver displayEventReceiver, long jitterNanos) {
int newPreferredIndex = 0;
// 计算新的最低可接受截止时间
final long minimumDeadline =
mFrameTimelines[mPreferredFrameTimelineIndex].mDeadlineNanos + jitterNanos;

// 1. 在现有时间轴中查找一个 deadline 尚未过期的时间轴
while (newPreferredIndex < mFrameTimelines.length - 1
&& mFrameTimelines[newPreferredIndex].mDeadlineNanos < minimumDeadline) {
newPreferredIndex++;
}

long newPreferredDeadline = mFrameTimelines[newPreferredIndex].mDeadlineNanos;

// 2. 如果现有的时间轴全部已经过期(主线程卡顿太久),则发起 Binder 跨进程调用
// 获取 SurfaceFlinger 端最新的 VsyncEventData
if (newPreferredDeadline < minimumDeadline) {
DisplayEventReceiver.VsyncEventData latestVsyncEventData =
displayEventReceiver.getLatestVsyncEventData();
if (latestVsyncEventData == null) {
Log.w(TAG, "无法获取最新的 VsyncEventData。SurfaceFlinger 是否崩溃了?");
} else {
update(frameTimeNanos, latestVsyncEventData);
}
} else {
// 3. 否则,直接更新为本地查找到的更靠后的有效时间轴
update(frameTimeNanos, newPreferredIndex);
}
return mFrameTimelines[mPreferredFrameTimelineIndex];
}

/** 内部状态更新 */
void update(long frameTimeNanos, int newPreferredFrameTimelineIndex) {
mFrameTimeNanos = frameTimeNanos;
mPreferredFrameTimelineIndex = newPreferredFrameTimelineIndex;
}
}

4. 遍历起点:ViewRootImpl.performTraversals()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
private void performTraversals() {
// 缓存 mView 到本地变量 host,因为在整个遍历过程中会频繁使用,且需要保证一致性
final View host = mView;
// 如果调试模式开启,打印调试信息
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}

// 检查 host 是否为空或者是否尚未添加,如果满足其中之一,记录原因并直接返回
if (host == null || !mAdded) {
mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added";
return;
}

// 如果存在同步请求导致的暂停计数,则记录 Trace 事件并跳过本次遍历
if (mNumPausedForSync > 0) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.instant(Trace.TRACE_TAG_VIEW,
TextUtils.formatSimple("performTraversals#mNumPausedForSync=%d",
mNumPausedForSync));
}
if (DEBUG_BLAST) {
Log.d(mTag, "Skipping traversal due to sync " + mNumPausedForSync);
}
mLastPerformTraversalsSkipDrawReason = "paused_for_sync";
return;
}

// 标记当前正在遍历中
mIsInTraversal = true;
// 标记即将进行绘制
mWillDrawSoon = true;
// 绘制取消标记及原因初始化
boolean cancelDraw = false;
String cancelReason = null;
// 是否为同步请求标记
boolean isSyncRequest = false;

// 窗口大小是否可能发生变化的标志位
boolean windowSizeMayChange = false;

// 获取窗口当前的布局参数
WindowManager.LayoutParams lp = mWindowAttributes;

// 期望的窗口宽高
int desiredWindowWidth;
int desiredWindowHeight;

// 获取当前视图可见性及其原因
final int viewVisibility = getHostVisibility();
final String viewVisibilityReason = getHostVisibilityReason();
// 检查可见性是否改变:非首次且 (记录的可见性不等 或 需要新 Surface 或 应用可见性改变)
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded
|| mAppVisibilityChanged);
// 重置应用可见性改变标记
mAppVisibilityChanged = false;
// 检查用户感知的可见性状态是否发生了实质性切换(VISIBLE 与非 VISIBLE 之间)
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
// 是否应该优化测量过程
final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp);

// 初始化 relayout 相关的参数和帧引用
WindowManager.LayoutParams params = null;
Rect frame = mWinFrame;
// 如果是第一次执行 performTraversals
if (mFirst) {
// 第一次必须执行完整重绘和布局
mFullRedrawNeeded = true;
mLayoutRequested = true;

// 获取资源配置
final Configuration config = getConfiguration();
// 根据布局参数确定期望的窗口尺寸
if (shouldUseDisplaySize(lp)) {
// 系统代码:直接获取屏幕真实物理尺寸
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
// Wrap content 情况,获取系统栏内边距后的可用边界
final Rect bounds = getWindowBoundsInsetSystemBars();
desiredWindowWidth = bounds.width();
desiredWindowHeight = bounds.height();
} else {
// 默认使用窗口管理器的 frameHint 尺寸,减少后续 relayout 导致的重测
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
}

// 现代窗口默认 32 位,设置绘制标志位
mAttachInfo.mUse32BitDrawingCache = true;
// 初始化 AttachInfo 中的可见性状态
mAttachInfo.mWindowVisibility = viewVisibility;
// 禁止全局属性重计算
mAttachInfo.mRecomputeGlobalAttributes = false;
// 备份当前配置
mLastConfigurationFromResources.setTo(config);
// 备份系统 UI 可见性
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
// 若初始方向为继承,则设置宿主视图的布局方向
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(config.getLayoutDirection());
}
// 分发视图附加到窗口的消息,这会触发所有子视图的 onAttachedToWindow
host.dispatchAttachedToWindow(mAttachInfo, 0);
// 通知 TreeObserver 窗口已附加
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
// 首次应用 Insets
dispatchApplyInsets(host);
// 如果未启用新版返回拦截且无自定义回调,则注册兼容性返回键回调
if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()
&& mWindowlessBackKeyCallback == null) {
registerCompatOnBackInvokedCallback();
}
} else {
// 非首次,直接取当前帧的宽高
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
// 如果宽高与当前记录的不符,标记需要布局和重绘
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}

// 处理可见性变化后的分发和资源清理
if (viewVisibilityChanged) {
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
mAttachInfo.mTreeObserver.dispatchOnWindowVisibilityChange(viewVisibility);
if (viewUserVisibilityChanged) {
host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
}
// 若不可见或需要新 Surface,结束缩放并销毁硬件资源
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
endDragResizing();
destroyHardwareResources();
}

// 可见时尝试提升帧率
if (shouldEnableDvrr() && viewVisibility == View.VISIBLE) {
boostFrameRate(FRAME_RATE_BOOST_TIME);
}
}

// 不可见窗口清理辅助功能焦点
if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
host.clearAccessibilityFocus();
}

// 执行运行队列中的动作(如 View.post)
getRunQueue().executeActions(mAttachInfo.mHandler);

// 首次遍历初始化触摸模式
if (mFirst) {
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
}

// 测量阶段:若请求了布局且非停止状态
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
if (!mFirst) {
// 非首次,针对 wrap content 再次评估期望宽高
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;

if (shouldUseDisplaySize(lp)) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
final Rect bounds = getWindowBoundsInsetSystemBars();
desiredWindowWidth = bounds.width();
desiredWindowHeight = bounds.height();
}
}
}

// 核心测量逻辑:计算视图树的大小
windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
}

// 收集并可能更新窗口属性
if (collectViewAttributes()) {
params = lp;
}
if (mAttachInfo.mForceReportNewAttributes) {
mAttachInfo.mForceReportNewAttributes = false;
params = lp;
}

// 软键盘调整模式的动态确定
if (mFirst || mAttachInfo.mViewVisibilityChanged) {
mAttachInfo.mViewVisibilityChanged = false;
int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST;
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
final int N = mAttachInfo.mScrollContainers.size();
for (int i=0; i<N; i++) {
if (mAttachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
if (resizeMode == 0) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
}
if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) {
lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode;
params = lp;
}
}
}

// Insets 变更后的重新分发和测量拦截
if (mApplyInsetsRequested) {
dispatchApplyInsets(host);
if (mLayoutRequested) {
windowSizeMayChange |= measureHierarchy(host, lp,
mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight,
shouldOptimizeMeasure);
}
}

// 清除标记,防止重复测量
if (layoutRequested) {
mLayoutRequested = false;
}

// 确定窗口是否真的需要 Resize(基于测量结果和属性)
boolean windowShouldResize = layoutRequested && windowSizeMayChange
&& ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing && mPendingDragResizing;

// 内部 Insets 计算标记
final boolean computesInternalInsets =
mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
|| mAttachInfo.mHasNonEmptyGivenInternalInsets;

boolean insetsPending = false;
int relayoutResult = 0;
boolean updatedConfiguration = false;

final int surfaceGenerationId = mSurface.getGenerationId();

final boolean isViewVisible = viewVisibility == View.VISIBLE;
boolean surfaceSizeChanged = false;
boolean surfaceCreated = false;
boolean surfaceDestroyed = false;
boolean surfaceReplaced = false;

// 检查属性变更
final boolean windowAttributesChanged = mWindowAttributesChanged;
if (windowAttributesChanged) {
mWindowAttributesChanged = false;
params = lp;
}

// relayout 前的参数适配
if (params != null) {
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0
&& !PixelFormat.formatHasAlpha(params.format)) {
params.format = PixelFormat.TRANSLUCENT;
}
adjustLayoutParamsForCompatibility(params,
mInsetsController.getAppearanceControlled(),
mInsetsController.isBehaviorControlled());
controlInsetsForCompatibility(params);
if (mDispatchedSystemBarAppearance != params.insetsFlags.appearance) {
mDispatchedSystemBarAppearance = params.insetsFlags.appearance;
mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance);
}
}

// 执行 relayoutWindow 与 WMS 通信的核心逻辑
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
TextUtils.formatSimple("%s-relayoutWindow#"
+ "first=%b/resize=%b/vis=%b/params=%b/force=%b", mTag,
mFirst, windowShouldResize, viewVisibilityChanged, params != null,
mForceNextWindowRelayout));
}

mForceNextWindowRelayout = false;

// Insets 挂起设置
insetsPending = computesInternalInsets
&& mWindowAttributes.providedInsets == null;

if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
}

boolean hwInitialized = false;
boolean dispatchApplyInsets = false;
boolean hadSurface = mSurface.isValid();

try {
if (DEBUG_LAYOUT) {
Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
host.getMeasuredHeight() + ", params=" + params);
}

if (mFirst || viewVisibilityChanged) {
mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED;
}
// 【核心 IPC】调用 Session.relayout 更新窗口状态
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
// 检查是否取消并重绘
cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
== RELAYOUT_RES_CANCEL_AND_REDRAW;
cancelReason = "relayout";
final boolean dragResizing = mPendingDragResizing;
// 处理同步请求
if (mSyncSeqId > mLastSyncSeqId) {
mLastSyncSeqId = mSyncSeqId;
if (DEBUG_BLAST) {
Log.d(mTag, "Relayout called with blastSync");
}
reportNextDraw("relayout");
mSyncBuffer = true;
isSyncRequest = true;
if (!cancelDraw) {
mDrewOnceForSync = false;
}
}

final boolean surfaceControlChanged =
(relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
== RELAYOUT_RES_SURFACE_CHANGED;

if (mSurfaceControl.isValid()) {
updateOpacity(mWindowAttributes, dragResizing,
surfaceControlChanged /*forceUpdate */);
}
} catch (RemoteException e) {
} finally {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

if (DEBUG_ORIENTATION) Log.v(
TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);

// 更新 AttachInfo 中的窗口偏移
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;

// 修正本地宽高记录
if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}

// 同步 SurfaceHolder 的回调和状态
if (mSurfaceHolder != null) {
if (mSurface.isValid()) {
mSurfaceHolder.mSurface = mSurface;
}
mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
mSurfaceHolder.mSurfaceLock.unlock();
if (surfaceCreated) {
mSurfaceHolder.ungetCallbacks();

mIsCreating = true;
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
}

if ((surfaceCreated || surfaceReplaced || surfaceSizeChanged
|| windowAttributesChanged) && mSurface.isValid()) {
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, lp.format,
mWidth, mHeight);
}
}
mIsCreating = false;
}

if (surfaceDestroyed) {
notifyHolderSurfaceDestroyed();
mSurfaceHolder.mSurfaceLock.lock();
try {
mSurfaceHolder.mSurface = new Surface();
} finally {
mSurfaceHolder.mSurfaceLock.unlock();
}
}
}

// 配置 ThreadedRenderer
final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer;
if (threadedRenderer != null && threadedRenderer.isEnabled()) {
if (hwInitialized
|| mWidth != threadedRenderer.getWidth()
|| mHeight != threadedRenderer.getHeight()
|| mNeedsRendererSetup) {
threadedRenderer.setup(mWidth, mHeight, mAttachInfo,
mWindowAttributes.surfaceInsets);
mNeedsRendererSetup = false;
}
}

// Relayout 后的最终测量:处理因 relayout 导致的尺寸差异
if (!mStopped || mReportNextDraw) {
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()
|| dispatchApplyInsets || updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
lp.privateFlags);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,
lp.privateFlags);

if (DEBUG_LAYOUT) Log.v(mTag, "糟糕,出了一些变化! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " dispatchApplyInsets=" + dispatchApplyInsets);

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

// 实现权重分配逻辑
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;

if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}

if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(mTag,
"嘿,让我们再测量一次:width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}

layoutRequested = true;
}
}
} else {
// 简单处理移动
maybeHandleWindowMove(frame);
}

// 处理之前推迟的测量
if (mViewMeasureDeferred) {
performMeasure(
MeasureSpec.makeMeasureSpec(frame.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(frame.height(), MeasureSpec.EXACTLY));
}

// 布局阶段判定
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);

if (didLayout) {
// 执行真正的布局分发
performLayout(lp, mWidth, mHeight);
}

// 处理焦点请求
if (mFirst) {
if (sAlwaysAssignFocus || !isInTouchMode()) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus());
}
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: requested focused view=" + mView.findFocus());
}
} else {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: existing focused view=" + mView.findFocus());
}
}
}
} else {
View focused = mView.findFocus();
if (focused instanceof ViewGroup
&& ((ViewGroup) focused).getDescendantFocusability()
== ViewGroup.FOCUS_AFTER_DESCENDANTS) {
focused.restoreDefaultFocus();
}
}

if (shouldEnableDvrr()) {
boostFrameRate(FRAME_RATE_BOOST_TIME);
}
}

// 触发辅助功能状态改变事件
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
if (changedVisibility) {
maybeFireAccessibilityWindowStateChangedEvent();
}

// 重置首次遍历和准备绘制标记
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mViewVisibility = viewVisibility;

// 通知 IME
final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);

// 首次 relayout 报告
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw("first_relayout");
}

mCheckIfCanDraw = isSyncRequest || cancelDraw;

// 【关键点】PreDraw 回调分发,允许在此拦截并取消绘制
boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
boolean cancelAndRedraw = cancelDueToPreDrawListener
|| (cancelDraw && mDrewOnceForSync);

// 如果不取消,准备同步和开始绘制通知
if (!cancelAndRedraw) {
if (mActiveSurfaceSyncGroup != null) {
mSyncBuffer = true;
}

createSyncIfNeeded();
notifyDrawStarted(isInWMSRequestedSync());
mDrewOnceForSync = true;

if (mActiveSurfaceSyncGroup != null && mSyncBuffer) {
updateSyncInProgressCount(mActiveSurfaceSyncGroup);
safeguardOverlappingSyncs(mActiveSurfaceSyncGroup);
}
}

// 最终绘制决策
if (!isViewVisible) {
// 不可见,跳过绘制
if (mLastTraversalWasVisible) {
logAndTrace("Not drawing due to not visible. Reason=" + viewVisibilityReason);
}
mLastPerformTraversalsSkipDrawReason = "view_not_visible";
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}

handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "view not visible");
} else if (cancelAndRedraw) {
// 被取消绘制,重新请求遍历
if (!mWasLastDrawCanceled) {
logAndTrace("Canceling draw."
+ " cancelDueToPreDrawListener=" + cancelDueToPreDrawListener
+ " cancelDueToSync=" + (cancelDraw && mDrewOnceForSync));
}
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
: "cancel_" + cancelReason;
scheduleTraversals();
} else {
// 执行绘制
if (mWasLastDrawCanceled) {
logAndTrace("Draw frame after cancel");
}
if (!mLastTraversalWasVisible) {
logAndTrace("Start draw after previous draw not visible");
}
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
// 【核心】执行绘制流程
if (!performDraw(mActiveSurfaceSyncGroup)) {
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, mLastPerformDrawSkippedReason);
}
}
// 状态位更新
mWasLastDrawCanceled = cancelAndRedraw;
mLastTraversalWasVisible = isViewVisible;

if (mAttachInfo.mContentCaptureEvents != null) {
notifyContentCaptureEvents();
}

mIsInTraversal = false;
mRelayoutRequested = false;

// 善后清理
if (!cancelAndRedraw) {
mReportNextDraw = false;
mLastReportNextDrawReason = null;
mActiveSurfaceSyncGroup = null;
if (mHasPendingTransactions) {
mPendingTransaction.apply();
mHasPendingTransactions = false;
}
mSyncBuffer = false;
if (isInWMSRequestedSync()) {
mWmsRequestSyncGroup.markSyncReady();
mWmsRequestSyncGroup = null;
mWmsRequestSyncGroupState = WMS_SYNC_NONE;
}
}

// VRR 帧率动态调整逻辑
if (mDrawnThisFrame) {
mDrawnThisFrame = false;
if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
mInvalidationIdleMessagePosted = true;
mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
}
// 设定最终的首选帧率和类别
setCategoryFromCategoryCounts();
updateInfrequentCount();
updateFrameRateFromThreadedRendererViews();
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
if (mPreferredFrameRate > 0
|| (mLastPreferredFrameRate != 0 && mPreferredFrameRate == 0)
) {
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
FRAME_RATE_SETTING_REEVALUATE_TIME);
}
// 计数器衰减
mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount;
mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0
? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
mPreferredFrameRate = -1;
mIsFrameRateConflicted = false;
mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
} else if (mPreferredFrameRate == 0) {
// 重置
setPreferredFrameRate(0);
mPreferredFrameRate = -1;
}
}

5. 指令录制:ThreadedRenderer.draw()

将 UI 线程的 Java 代码绘制逻辑录制为 Native 层的 DisplayList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 绘制指定的视图。
*
* @param view 要绘制的视图(通常是 DecorView)。
* @param attachInfo 绑定到该视图的 AttachInfo 包含渲染所需的状态信息。
* @param callbacks 绘制过程中的回调接口。
*/
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
// 1. 记录绘制开始的时间点,用于性能监控和 FrameMetrics 统计
attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart();

// 2. 更新根显示列表(DisplayList)。
// 这一步会遍历视图树,录制每个 View 的绘制指令(如 drawRect, drawText 等)。
updateRootDisplayList(view, callbacks);

// 3. 处理在渲染器创建之前就开始执行的动画。
// 典型的场景是在第一帧绘制前就启动了动画,这些动画节点需要注册到当前的硬件渲染器中。
if (attachInfo.mPendingAnimatingRenderNodes != null) {
final int count = attachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
// 将待处理的动画渲染节点注册到渲染引擎
registerAnimatingRenderNode(
attachInfo.mPendingAnimatingRenderNodes.get(i));
}
// 清空并释放待处理列表
attachInfo.mPendingAnimatingRenderNodes.clear();
// 之后的动画会通过 ViewRootImpl#attachRenderNodeAnimator 直接注册,不再需要此暂存列表
attachInfo.mPendingAnimatingRenderNodes = null;
}

// 4. 获取当前帧最新的信息(包含各种时间戳和状态)
final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo();

// 5. 核心步骤:同步并绘制。
// 此函数会将 UI 线程录制的绘制信息“同步”到渲染线程(RenderThread),
// 并触发 GPU 开始执行实际的绘制操作。
int syncResult = syncAndDrawFrame(frameInfo);

// 6. 错误处理:如果 Surface 丢失
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
Log.w("HWUI", "Surface 丢失,强制进行重新布局 (relayout)");
// 标记下次遍历时强制执行 WindowManager 的 relayout,
// 期望从中获取一个新的、有效的 Surface。
attachInfo.mViewRootImpl.mForceNextWindowRelayout = true;
attachInfo.mViewRootImpl.requestLayout();
}

// 7. 调度重绘:如果同步结果要求重绘
// 例如:动画正在运行,或者渲染引擎需要再画一帧来更新状态。
if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
}

二、 Native 层:RenderThread 异步渲染 (frameworks/base/libs/hwui/)

1. 资源同步:CanvasContext::prepareTree()

在正式绘制前,将 UI 线程录制的变化应用到 Native 渲染树中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* 准备渲染树(Sync 阶段)。
* 此方法在渲染线程(RenderThread)中执行,负责将 UI 线程的变更同步到渲染节点。
*
* @param info 渲染树信息,用于存储同步过程中的各种状态(如是否有动画、脏区等)
* @param uiFrameInfo 指向 UI 线程传递过来的帧时间戳数据的指针
* @param syncQueued 同步请求进入队列的时间点
* @param target 目标渲染节点(通常是根节点)
*/
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued,
RenderNode* target) {
// 1. 清除之前可能存在的帧回调,准备开始新的帧流程
mRenderThread.removeFrameCallback(this);

// 2. 检查上一帧是否被跳过,并维护跳帧信息
const auto reason = wasSkipped(mCurrentFrameInfo);
if (reason.has_value()) {
if (!mSkippedFrameInfo) {
switch (*reason) {
case SkippedFrameReason::AlreadyDrawn:
case SkippedFrameReason::NoBuffer:
case SkippedFrameReason::NoOutputTarget:
// 记录因技术原因(非业务逻辑)跳过的帧信息,用于后续的 FrameTimeline 统计
mSkippedFrameInfo.emplace();
mSkippedFrameInfo->vsyncId =
mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
mSkippedFrameInfo->startTime =
mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
break;
case SkippedFrameReason::DrawingOff:
case SkippedFrameReason::ContextIsStopped:
case SkippedFrameReason::NothingToDraw:
break;
}
}
} else {
// 如果上一帧正常执行,则开启 JankTracker 对新一帧的监控
mCurrentFrameInfo = mJankTracker.startFrame();
mSkippedFrameInfo.reset();
}

// 3. 导入 UI 线程的时间信息,并记录同步开始的时间戳
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
mCurrentFrameInfo->markSyncStart();

// 4. 初始化同步环境
info.damageAccumulator = &mDamageAccumulator; // 用于累加每一层产生的脏区
info.layerUpdateQueue = &mLayerUpdateQueue; // 待更新的硬件图层队列
info.damageGenerationId = mDamageId++;
info.out.skippedFrameReason = std::nullopt;

// 5. 遍历渲染节点树,执行实际的同步操作(核心递归逻辑)
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
// 如果节点是目标节点,执行完整同步;否则仅同步渲染线程相关的部分
info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
node->prepareTree(info); // 这里会递归调用所有子 View 的同步逻辑
GL_CHECKPOINT(MODERATE);
}
// 运行剩余的渲染线程动画(如 RenderNodeAnimator)
mAnimationContext->runRemainingAnimations(info);
GL_CHECKPOINT(MODERATE);

// 6. 释放预取但不再需要的图层
freePrefetchedLayers();
GL_CHECKPOINT(MODERATE);

mIsDirty = true;

// 7. 各种跳帧预判(性能优化)

// 7.1 检查是否有渲染输出目标(Surface)
if (CC_UNLIKELY(!hasOutputTarget())) {
info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
return;
}

// 7.2 检查是否已经在极短时间内(2ms内)画过同一帧 Vsync,避免重复渲染
if (CC_LIKELY(mSwapHistory.size() && !info.forceDrawFrame)) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
SwapHistory& lastSwap = mSwapHistory.back();
nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
if (vsyncDelta < 2_ms) {
info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn;
}
} else {
info.out.skippedFrameReason = std::nullopt;
}

// 7.3 检查内容是否可渲染(例如只有 2 个节点且第二个节点内容为空)
if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) {
info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw;
}

// 8. 关键步骤:尝试从 BufferQueue 预留(reserve)一个缓冲区
if (!info.out.skippedFrameReason) {
int err = mNativeSurface->reserveNext();
if (err != OK) {
// 如果拿不到 Buffer(通常是 SurfaceFlinger 消费太慢),标记本帧跳过
info.out.skippedFrameReason = SkippedFrameReason::NoBuffer;
mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
ALOGW("reserveNext 失败,错误 = %d (%s)", err, strerror(-err));
if (err != TIMED_OUT) {
setSurface(nullptr); // 严重错误则断开 Surface
return;
}
}
} else {
mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
}

// 9. 处理动画和重绘逻辑
bool postedFrameCallback = false;
if (info.out.hasAnimations || info.out.skippedFrameReason) {
// 如果渲染线程有动画,或者由于某种原因本帧没画,需要请求下一帧
if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
info.out.requiresUiRedraw = true;
}
if (!info.out.requiresUiRedraw) {
// 在渲染线程注册一个 Vsync 回调,以驱动动画持续运行
mRenderThread.postFrameCallback(this);
postedFrameCallback = true;
}
}

// 10. 处理动图(如 GIF/WebP)的播放延迟逻辑
if (!postedFrameCallback &&
info.out.animatedImageDelay != TreeInfo::Out::kNoAnimatedImageDelay) {
const nsecs_t kFrameTime = mRenderThread.timeLord().frameIntervalNanos();
if (info.out.animatedImageDelay <= kFrameTime) {
// 如果延迟小于一帧时间,立即请求下一帧
mRenderThread.postFrameCallback(this);
} else {
// 否则,在指定延迟后请求重绘
const auto delay = info.out.animatedImageDelay - kFrameTime;
int genId = mGenerationID;
mRenderThread.queue().postDelayed(delay, [this, genId]() {
if (mGenerationID == genId) {
mRenderThread.postFrameCallback(this);
}
});
}
}
}

2. 绘制与提交:CanvasContext::draw()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/**
* 执行实际的绘制操作。
* 这是渲染管线的核心阶段:计算脏区 -> 调用 GPU 绘制 -> 交换缓冲区 -> 报告性能指标。
*
* @param solelyTextureViewUpdates 是否仅更新 TextureView(影响性能提示策略)
*/
void CanvasContext::draw(bool solelyTextureViewUpdates) {
#ifdef __ANDROID__
// 1. 安全检查:确保 GPU 上下文(GrContext)可用且未丢失
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
if (grContext->isDeviceLost()) {
LOG_ALWAYS_FATAL("意外丢失 GPU 设备");
return;
}
LOG_ALWAYS_FATAL("在 CanvasContext::draw 开始时 GrContext 已被放弃");
return;
}
}
#endif

// 2. 计算并获取当前帧的脏区(需要重新绘制的矩形区域)
SkRect dirty;
mDamageAccumulator.finish(&dirty);

// 暂存并重置同步延迟和空闲时长(用于后续性能计算)
nsecs_t syncDelayDuration = mSyncDelayDuration;
nsecs_t idleDuration = mIdleDuration;
mSyncDelayDuration = 0;
mIdleDuration = 0;

// 3. 跳帧逻辑检查:判断这一帧是否真的需要绘制
const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> {
if (!Properties::isDrawingEnabled()) {
return SkippedFrameReason::DrawingOff; // 全局绘制已关闭
}
// 如果脏区为空,且允许跳过空帧,且 Surface 不需要强制重绘,则跳过
if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
return SkippedFrameReason::NothingToDraw;
}
return std::nullopt;
}();

if (skippedFrameReason) {
// 如果决定跳过本帧,记录原因并清理状态
mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
#ifdef __ANDROID__
if (auto grContext = getGrContext()) {
grContext->flushAndSubmit(); // 依然提交之前的异步任务
}
#endif
waitOnFences(); // 等待之前的栅栏同步完成
for (auto& func : mFrameCommitCallbacks) {
std::invoke(func, false /* didProduceBuffer: 未生成新缓冲区 */);
}
mFrameCommitCallbacks.clear();
return;
}

// 4. 开始正式绘制流程
ScopedActiveContext activeContext(this);
mCurrentFrameInfo->set(FrameInfoIndex::FrameInterval) =
mRenderThread.timeLord().frameIntervalNanos();

// 标记:开始向 GPU 发送绘制命令
mCurrentFrameInfo->markIssueDrawCommandsStart();

// 获取当前待渲染的 Frame 对象(此处触发 Surface::dequeueBuffer(),是潜在的强阻塞点)
Frame frame = getFrame();

// 计算相对于窗口的最终脏区
SkRect windowDirty = computeDirtyRect(frame, &dirty);

ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));

// 5. 调用渲染管线(SkiaGL/Vulkan)执行真正的绘制命令提交
IRenderPipeline::DrawResult drawResult;
{
drawResult = mRenderPipeline->draw(
frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds,
mOpaque, mLightInfo, mRenderNodes, &(profiler()), mBufferParams, profilerLock());
}

uint64_t frameCompleteNr = getFrameNumber();

// 等待 GPU 指令执行的同步信号
waitOnFences();

// 6. 向底层系统窗口设置帧时间轴信息(用于同步 Vsync 和处理输入事件延迟)
if (mNativeSurface) {
const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
const ANativeWindowFrameTimelineInfo ftl = {
.frameNumber = frameCompleteNr,
.frameTimelineVsyncId = vsyncId,
.inputEventId = inputEventId,
.startTimeNanos = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
.useForRefreshRateSelection = solelyTextureViewUpdates,
.skippedFrameVsyncId = mSkippedFrameInfo ? mSkippedFrameInfo->vsyncId
: UiFrameInfoBuilder::INVALID_VSYNC_ID,
.skippedFrameStartTimeNanos =
mSkippedFrameInfo ? mSkippedFrameInfo->startTime : 0,
};
native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), ftl);
}
}

bool requireSwap = false; // 是否需要交换缓冲区(将画面提交给屏幕)
bool didDraw = false; // 是否成功绘制

// 7. 提交缓冲区(Swap Buffers)
int error = OK;
bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty, mCurrentFrameInfo,
&requireSwap);

// 记录 GPU 指令提交完成的时间点
mCurrentFrameInfo->set(FrameInfoIndex::CommandSubmissionCompleted) = std::max(
drawResult.commandSubmissionTime, mCurrentFrameInfo->get(FrameInfoIndex::SwapBuffers));

mIsDirty = false;

if (requireSwap) {
didDraw = true;
error = mNativeSurface->getAndClearError();
if (error == TIMED_OUT) {
// 如果获取 Buffer 超时,请求下一帧重试
mRenderThread.postFrameCallback(this);
didDraw = false;
} else if (error != OK || !didSwap) {
// 如果发生严重错误,断开 Surface 绑定
setSurface(nullptr);
didDraw = false;
}

// 记录交换历史,用于性能追溯
SwapHistory& swap = mSwapHistory.next();
if (didDraw) {
swap.damage = windowDirty;
} else {
float max = static_cast<float>(INT_MAX);
swap.damage = SkRect::MakeWH(max, max);
}
swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();

// 记录 Dequeue(出队)和 Queue(入队)Buffer 的耗时,这是 Jank 的常见原因
if (didDraw) {
nsecs_t dequeueStart =
ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow());
if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
swap.dequeueDuration = 0;
} else {
swap.dequeueDuration =
ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow());
}
swap.queueDuration =
ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow());
} else {
swap.dequeueDuration = 0;
swap.queueDuration = 0;
}
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
mHaveNewSurface = false;
mFrameNumber = 0;
} else {
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
}

// 标记:缓冲区交换操作完成
mCurrentFrameInfo->markSwapBuffersCompleted();

// 如果开启了调试宏,计算并打印移动平均帧耗时
#if LOG_FRAMETIME_MMA
float thisFrame = mCurrentFrameInfo->duration(FrameInfoIndex::IssueDrawCommandsStart,
FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F;
if (sFrameCount) {
sBenchMma = ((9 * sBenchMma) + thisFrame) / 10;
} else {
sBenchMma = thisFrame;
}
if (++sFrameCount == 10) {
sFrameCount = 1;
ALOGD("平均帧耗时: %.4f", sBenchMma);
}
#endif
// 执行所有注册的帧提交回调(通知应用层这一帧已经发给系统了)
if (didSwap) {
for (auto& func : mFrameCommitCallbacks) {
std::invoke(func, true /* didProduceBuffer */);
}
mFrameCommitCallbacks.clear();
}

// 8. 上报 Jank(卡顿)数据和帧指标
if (requireSwap) {
if (mExpectSurfaceStats) {
// 如果支持 SurfaceStats,则异步报告包含 Present Time(实际显示时间)的指标
reportMetricsWithPresentTime();
{
std::lock_guard lock(mLast4FrameMetricsInfosMutex);
FrameMetricsInfo& next = mLast4FrameMetricsInfos.next();
next.frameInfo = mCurrentFrameInfo;
next.frameNumber = frameCompleteNr;
next.surfaceId = mSurfaceControlGenerationId;
}
} else {
// 否则直接结束当前帧记录
mCurrentFrameInfo->markFrameCompleted();
mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
= mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
std::scoped_lock lock(mFrameInfoMutex);
mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr,
mSurfaceControlGenerationId);
}
}

// 9. 性能调节:向系统 HintSession 反馈实际工作负载
int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);

// 更新目标耗时(截止时间 - 预期 Vsync 时间)
mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);

if (didDraw) {
int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
// 计算实际 CPU 工作耗时,排除掉等待 Buffer 和空闲的时间,以便更准地调整 CPU 频率
int64_t actualDuration = frameDuration -
(std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
dequeueBufferDuration - idleDuration;
mHintSessionWrapper->reportActualWorkDuration(actualDuration);

// 如果有 WebView 渲染线程,也一并通知系统这些线程的活跃状态
mHintSessionWrapper->setActiveFunctorThreads(
WebViewFunctorManager::instance().getRenderingThreadsForActiveFunctors());
}

mLastDequeueBufferDuration = dequeueBufferDuration;

// 清理缓存管理器中的过期资源
mRenderThread.cacheManager().onFrameCompleted();
return;
}

三、 底层图形系统 (frameworks/native/libs/gui/)

关键阻塞点:Surface::dequeueBuffer()

该函数在 CanvasContext::draw() 执行过程中被调用(对应源码中的 Frame frame = getFrame(); 这一行),用于向 BufferQueue 索要一个可写的图形缓冲区。

  • 阻塞逻辑: 如果当前所有的 Buffer 都在 SurfaceFlinger 端等待显示,或者 GPU 上一帧还没跑完导致没有空闲 Buffer,渲染线程会在此处挂起。这是全链路中最著名的潜在阻塞点
  • 监控: 通过 dumpsys gfxinfo 查看到的 Dequeue Buffer 耗时(或 Trace 中的 DequeueBufferDuration)即代表此处的等待时长。较高的数值通常意味着系统负载极重、SurfaceFlinger 合成压力大或 GPU 性能达到瓶颈。

四、 性能指标解析

1. UNKNOWN_DELAY_DURATION 溯源

FrameMetrics 中,该指标反映了帧处理流程开始前的不明延迟。

  • 计算公式: UNKNOWN_DELAY_DURATION = Index.HANDLE_INPUT_START - Index.INTENDED_VSYNC
  • 含义: 代表从系统预期发送 Vsync 信号到应用真正开始处理输入事件之间的空隙。这通常是因为主线程被其他非 UI 任务(如广播处理、Handler 消息)占用,导致无法第一时间响应渲染信号。

五:JankTracker 源代码(Android 15)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#include "JankTracker.h"

#include <cutils/ashmem.h>
#include <cutils/trace.h>
#include <errno.h>
#include <inttypes.h>
#include <log/log.h>

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
#include <sstream>

#include "DeviceInfo.h"
#include "Properties.h"
#include "utils/TimeUtils.h"
#include "utils/Trace.h"

namespace android {
namespace uirenderer {

// 定义各类卡顿检测的比较规则
struct Comparison {
JankType type; // 卡顿类型
std::function<int64_t(nsecs_t)> computeThreadshold; // 计算阈值的函数
FrameInfoIndex start; // 起始时间点索引
FrameInfoIndex end; // 结束时间点索引
};

// 具体的卡顿判定标准
static const std::array<Comparison, 4> COMPARISONS{
// 1. 错过 Vsync:预期启动时间与实际启动时间偏差 > 1ns 即视为错过
Comparison{JankType::kMissedVsync, [](nsecs_t) { return 1; }, FrameInfoIndex::IntendedVsync,
FrameInfoIndex::Vsync},

// 2. UI 线程慢:从 Vsync 信号到 Sync 开始,耗时超过 0.5 倍帧间隔
Comparison{JankType::kSlowUI,
[](nsecs_t frameInterval) { return static_cast<int64_t>(.5 * frameInterval); },
FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},

// 3. Sync 阶段慢:主线程与渲染线程数据同步耗时超过 0.2 倍帧间隔
Comparison{JankType::kSlowSync,
[](nsecs_t frameInterval) { return static_cast<int64_t>(.2 * frameInterval); },
FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},

// 4. 渲染线程慢:从提交命令到帧完成耗时超过 0.75 倍帧间隔
Comparison{JankType::kSlowRT,
[](nsecs_t frameInterval) { return static_cast<int64_t>(.75 * frameInterval); },
FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
};

// 如果单帧耗时超过 10 秒,则认为不是普通的卡顿而是 ANR,不再计入卡顿统计
static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);

/*
* 目前不跟踪通过 Surface:lockHardwareCanvas() 直接进行的绘制。
* TODO: kSurfaceCanvas 会占用渲染线程时间,可能负面影响其他绘制,未来需要研究如何归因。
*/
static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;

// 测试环境下,过滤掉帧起始阶段的未知延迟,将其视为测试基建的开销
static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;

JankTracker::JankTracker(ProfileDataContainer* globalData)
: mData(globalData->getDataMutex())
, mDataMutex(globalData->getDataMutex()) {
mGlobalData = globalData;
nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
nsecs_t sfOffset = DeviceInfo::getCompositorOffset();
nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset();

// 根据 App 偏移和 SF 偏移的差值,计算 dequeueBuffer 阻塞的容忍时间(Legacy)
if (offsetDelta <= 4_ms && offsetDelta >= 0) {
// SF 将在 VSYNC-app + offsetDelta 开始合成。
// 在三缓冲情况下,这是由于 VSYNC-app 和 VSYNC-sf 交错而预期 dequeueBuffer 返回的时间。
mDequeueTimeForgivenessLegacy = offsetDelta + 4_ms;
}
mFrameIntervalLegacy = frameIntervalNanos;
}

/**
* 计算旧版(Legacy)卡顿指标。
* 主要处理 dequeueBuffer 阻塞时间的宽赦逻辑,避免因为 Buffer 调度导致的误判。
*/
void JankTracker::calculateLegacyJank(FrameInfo& frame) REQUIRES(mDataMutex) {
// 基础总耗时:从预期开始到 Swap 完成
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted);

// 如果存在 dequeueBuffer 耗时(>500us),尝试进行“宽赦”
if (mDequeueTimeForgivenessLegacy && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
// 预期 dequeue 时间 = 偏移容忍值 + Vsync时间 - 发送绘制命令的时间
nsecs_t expectedDequeueDuration = mDequeueTimeForgivenessLegacy
+ frame[FrameInfoIndex::Vsync]
- frame[FrameInfoIndex::IssueDrawCommandsStart];
if (expectedDequeueDuration > 0) {
// 宽赦量取“预期阻塞值”和“实际阻塞值”的较小者
nsecs_t forgiveAmount =
std::min(expectedDequeueDuration, frame[FrameInfoIndex::DequeueBufferDuration]);
if (forgiveAmount >= totalDuration) {
ALOGV("不可能的出队耗时! reported %" PRId64 ", total %" PRId64,
forgiveAmount, totalDuration);
return;
}
// 从总时长中减去宽赦的部分
totalDuration -= forgiveAmount;
}
}

if (totalDuration <= 0) {
ALOGV("总时长异常 %" PRId64 " start=%" PRIi64 " gpuComplete=%" PRIi64,
totalDuration, frame[FrameInfoIndex::IntendedVsync],
frame[FrameInfoIndex::GpuCompleted]);
return;
}

// 排除特殊标记的帧(如 lockHardwareCanvas)
if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) {
return;
}

// 如果扣除宽赦后依然超过 16.6ms(对于 60Hz),记为卡顿
if (totalDuration > mFrameIntervalLegacy) {
mData->reportJankLegacy();
(*mGlobalData)->reportJankLegacy();
}

// 更新 Swap 截止时间
if (mSwapDeadlineLegacy < 0) {
mSwapDeadlineLegacy = frame[FrameInfoIndex::IntendedVsync] + mFrameIntervalLegacy;
}

// 判断是否处于三缓冲状态:当前截止时间与预期开始时间的偏差 > 0.1倍帧间隔
bool isTripleBuffered = (mSwapDeadlineLegacy - frame[FrameInfoIndex::IntendedVsync])
> (mFrameIntervalLegacy * 0.1);

mSwapDeadlineLegacy = std::max(mSwapDeadlineLegacy + mFrameIntervalLegacy,
frame[FrameInfoIndex::IntendedVsync] + mFrameIntervalLegacy);

// 如果在截止时间内完成,或总耗时小于帧间隔,说明没有产生肉眼可见的卡顿
if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadlineLegacy
|| totalDuration < mFrameIntervalLegacy) {
if (isTripleBuffered) {
// 虽然没卡顿,但如果触发了三缓冲,属于高延迟状态
mData->reportJankType(JankType::kHighInputLatency);
(*mGlobalData)->reportJankType(JankType::kHighInputLatency);
}
return;
}

// 否则记为错过旧版截止日期
mData->reportJankType(JankType::kMissedDeadlineLegacy);
(*mGlobalData)->reportJankType(JankType::kMissedDeadlineLegacy);

// 发生卡顿,根据实际完成时间重置下一个周期的截止时间
nsecs_t jitterNanos = frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::Vsync];
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalLegacy;
mSwapDeadlineLegacy = frame[FrameInfoIndex::FrameCompleted]
- lastFrameOffset + mFrameIntervalLegacy;
}

/**
* 帧渲染结束后的统计入口。
* @param frame 包含帧各阶段时间戳的信息
*/
void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
int64_t frameNumber, int32_t surfaceControlId) {
std::lock_guard lock(mDataMutex);

// 1. 先计算旧版卡顿指标
calculateLegacyJank(frame);

// 2. 计算当前帧的总时长(从预期 Vsync 到帧完成)
int64_t totalDuration = frame.duration(FrameInfoIndex::IntendedVsync,
FrameInfoIndex::FrameCompleted);

if (totalDuration <= 0) {
ALOGV("总时长异常 %" PRId64, totalDuration);
return;
}

// 上报帧时长数据到直方图
mData->reportFrame(totalDuration);
(*mGlobalData)->reportFrame(totalDuration);

if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) {
return;
}

int64_t frameInterval = frame[FrameInfoIndex::FrameInterval];

// 判断是否触发“三缓冲”(Buffer Stuffing)
// 如果本帧开始时间早于(假设未发生堆积时的)预期开始时间,说明前面有帧堆积了
bool isTripleBuffered = (mNextFrameStartUnstuffed - frame[FrameInfoIndex::IntendedVsync])
> (frameInterval * 0.1);

int64_t deadline = frame[FrameInfoIndex::FrameDeadline];

// 在三缓冲情况下,队列中有足够的缓冲区来支撑一次掉帧而不产生卡顿
// 因此根据堆积情况动态调整截止日期判定
if (isTripleBuffered) {
int64_t originalDeadlineDuration = deadline - frame[FrameInfoIndex::IntendedVsync];
deadline = mNextFrameStartUnstuffed + originalDeadlineDuration;
frame.set(FrameInfoIndex::FrameDeadline) = deadline;
}

// 3. 检查是否达到了性能截止日期 (Deadline)
if (frame[FrameInfoIndex::GpuCompleted] < deadline) {
if (isTripleBuffered) {
// 虽未掉帧,但属于高延迟输入
mData->reportJankType(JankType::kHighInputLatency);
(*mGlobalData)->reportJankType(JankType::kHighInputLatency);

// 除非有明显的“暂停”,否则三缓冲状态会延续到下一帧
mNextFrameStartUnstuffed += frameInterval;
}
} else {
// 判定为掉帧 (Jank)
mData->reportJankType(JankType::kMissedDeadline);
(*mGlobalData)->reportJankType(JankType::kMissedDeadline);
mData->reportJank();
(*mGlobalData)->reportJank();

// 计算抖动偏移,用于修正下一帧的期望开始时间
nsecs_t jitterNanos = frame[FrameInfoIndex::GpuCompleted]
- frame[FrameInfoIndex::Vsync];
nsecs_t lastFrameOffset = jitterNanos % frameInterval;

// 记录在非堆积情况下,下一帧应该开始的时间
mNextFrameStartUnstuffed = frame[FrameInfoIndex::GpuCompleted]
- lastFrameOffset + frameInterval;

// 4. 卡顿溯源分析:遍历所有的 COMPARISONS 规则,找出是哪个阶段慢了
recomputeThresholds(frameInterval);
for (auto& comparison : COMPARISONS) {
int64_t delta = frame.duration(comparison.start, comparison.end);
// 如果某阶段耗时超过对应阈值,则上报该类型的卡顿原因
if (delta >= mThresholds[comparison.type] && delta < IGNORE_EXCEEDING) {
mData->reportJankType(comparison.type);
(*mGlobalData)->reportJankType(comparison.type);
}
}

// 5. "Davey" 严重卡顿日志
// 如果一帧耗时超过 700ms,打印极其详细的时间轴数据辅助排查
if (totalDuration >= 700_ms) {
static int sDaveyCount = 0;
std::stringstream ss;
ss << "Davey! duration=" << ns2ms(totalDuration) << "ms; ";
for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
ss << FrameInfoNames[i] << "=" << frame[i] << ", ";
}
ALOGI("%s", ss.str().c_str());
// 并在 Trace 中标记
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
}
}

// 6. 上报 GPU 绘制时长(如果平台支持)
int64_t totalGPUDrawTime = frame.gpuDrawTime();
if (totalGPUDrawTime >= 0) {
mData->reportGPUFrame(totalGPUDrawTime);
(*mGlobalData)->reportGPUFrame(totalGPUDrawTime);
}

// 7. 如果注册了 Metrics 监听器(如 FrameMetrics API),同步上报底层数据
if (CC_UNLIKELY(reporter.get() != nullptr)) {
reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */, frameNumber,
surfaceControlId);
}
}

/**
* 根据帧预算(取决于刷新率)重新计算各阶段的卡顿判定阈值
*/
void JankTracker::recomputeThresholds(int64_t frameBudget) REQUIRES(mDataMutex) {
if (mThresholdsFrameBudget == frameBudget) {
return;
}
mThresholdsFrameBudget = frameBudget;
for (auto& comparison : COMPARISONS) {
// 调用每个 Comparison 规则自带的 lambda 函数计算阈值
mThresholds[comparison.type] = comparison.computeThreadshold(frameBudget);
}
}

/**
* 将卡顿统计数据导出
*/
void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
const ProfileData* data) {
#ifdef __ANDROID__
if (description) {
switch (description->type) {
case JankTrackerType::Generic:
break;
case JankTrackerType::Package:
dprintf(fd, "\nPackage: %s", description->name.c_str());
break;
case JankTrackerType::Window:
dprintf(fd, "\nWindow: %s", description->name.c_str());
break;
}
}
if (sFrameStart != FrameInfoIndex::IntendedVsync) {
dprintf(fd, "\nNote: Data has been filtered!");
}
data->dump(fd);
dprintf(fd, "\n");
#endif
}

/**
* 打印最近所有帧的时间戳详细数据
*/
void JankTracker::dumpFrames(int fd) {
#ifdef __ANDROID__
dprintf(fd, "\n\n---PROFILEDATA---\n");
for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
dprintf(fd, "%s", FrameInfoNames[i]);
dprintf(fd, ",");
}
for (size_t i = 0; i < mFrames.size(); i++) {
FrameInfo& frame = mFrames[i];
if (frame[FrameInfoIndex::SyncStart] == 0) {
continue;
}
dprintf(fd, "\n");
for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
dprintf(fd, "%" PRId64 ",", frame[i]);
}
}
dprintf(fd, "\n---PROFILEDATA---\n\n");
#endif
}

/**
* 重置卡顿统计
*/
void JankTracker::reset() REQUIRES(mDataMutex) {
mFrames.clear();
mData->reset();
(*mGlobalData)->reset();
// 根据系统属性决定是否过滤掉测试导致的起始开销
sFrameStart = Properties::filterOutTestOverhead ? FrameInfoIndex::HandleInputStart
: FrameInfoIndex::IntendedVsync;
}

} /* namespace uirenderer */
} /* namespace android */

,