Fork me on GitHub
侧边栏

Android Jank 检测与 FrameTimeline

如果一帧的实际显示时间与调度器预测的呈现时间不匹配,则该帧被称为"卡顿帧"(janky)。

卡顿可能导致:

  • 帧率不稳定
  • 延迟增加

FrameTimeline是SurfaceFlinger中的一个模块,用于检测卡顿并报告卡顿来源。目前暂不支持SurfaceView,但未来版本将会支持。

用户界面:

系统会为每个至少有一帧显示在屏幕上的应用程序新增两条时间线轨迹。

image

Expected Timeline

每个时间片代表系统分配给应用渲染帧的时间。为避免系统出现卡顿,应用应在该时间范围内完成渲染。起始时间为Choreographer回调被调度执行的时间。

Actual Timeline

这些时间片表示应用实际完成帧渲染(包括GPU工作)并将帧发送至SurfaceFlinger进行合成的耗时。起始时间为Choreographer#doFrame或AChoreographer_vsyncCallback开始执行的时间。此处时间片的结束时间取GPU耗时与提交耗时的最大值。提交耗时是指应用帧被提交至SurfaceFlinger的时间。

image

同样地,SurfaceFlinger 也会新增这两条时间线轨道,分别表示其预期完成合成工作的时间范围,以及实际完成帧合成并显示到屏幕所耗费的时间。这里,SurfaceFlinger 的工作涵盖了显示栈中位于其下层的所有组件,包括 Composer 和 DisplayHAL。因此,这些时间片代表了从 SurfaceFlinger 主线程开始到屏幕更新完成的整个过程。

每个时间片的名称对应从 Choreographer 接收到的令牌(token)。你可以将实际时间线轨道中的某个时间片与其在预期时间线轨道中的对应时间片进行对比,从而观察应用的实际表现是否符合预期。此外,为了便于调试,该令牌也会被添加到应用的 doFrame 和 RenderThread 时间片中。而对于 SurfaceFlinger 来说,相同的令牌也会显示在 onMessageReceived 事件中。

image

image

选择实际时间线时间片

image

选择详情
提供该帧的更多相关信息,包括:

  • 呈现类型
    该帧是提前、准时还是延迟呈现。
  • 准时完成
    应用是否按时完成了该帧的渲染工作。
  • 卡顿类型
    该帧是否出现卡顿?若存在卡顿,会显示具体类型;若无卡顿,则显示“无”。
  • 预测类型
    当该帧被 FrameTimeline 接收时,预测是否已过期?若过期,则显示“过期预测”;否则显示“有效预测”。
  • GPU 合成
    布尔值,表示该帧是否由 GPU 合成。
  • 图层名称
    该帧所呈现到的图层/表面名称。某些进程会向多个表面更新帧,在实际时间线中会显示具有相同令牌的多个时间片。图层名称可用于区分这些时间片。
  • 是否为缓冲区
    布尔值,表示该帧对应的是缓冲区还是动画。

Flow events

在应用中选择实际时间线时间片时,会绘制一条连线指向对应的 SurfaceFlinger 时间线时间片。

image

由于SurfaceFlinger可以将来自多个图层的帧合成为单个屏幕帧(称为DisplayFrame),因此选择DisplayFrame时会绘制箭头指向所有被合成的帧。这些帧可能跨越多个进程。

image

image

Color codes

颜色 图像 描述
绿色 img 良好帧。未观察到卡顿
浅绿色 img 高延迟状态。帧率流畅但帧呈现延迟,导致输入延迟增加
红色 img 卡顿帧。该时间片所属的进程是造成卡顿的原因
黄色 img 仅由应用使用。帧存在卡顿,但应用不是原因,SurfaceFlinger导致了卡顿
蓝色 img 丢弃帧。与卡顿无关。该帧被SurfaceFlinger丢弃,系统优先选择了更新后的帧

卡顿类型解析

卡顿类型定义于 JankInfo.h 中。由于每个应用的实现方式不同,无法统一深入应用内部具体分析卡顿原因。我们的目标并非追溯根本原因,而是快速判断卡顿是由应用还是 SurfaceFlinger 引起。


无卡顿

  • None
    一切正常,帧无卡顿。这是理想状态,应以此为目标。

应用卡顿

  • AppDeadlineMissed
    应用运行超时导致卡顿。应用帧的总耗时通过以 Choreographer 唤醒为起始时间、以 max(GPU 时间, 提交时间) 为结束时间计算得出。提交时间指帧发送至 SurfaceFlinger 的时间。由于 GPU 通常并行运行,可能出现 GPU 完成时间晚于提交时间的情况。
  • BufferStuffing
    这更像是一种状态而非严格意义上的卡顿。当应用在上一帧尚未呈现时就持续向 SurfaceFlinger 发送新帧,内部缓冲队列会被未呈现的缓冲区填满(因此得名“缓冲填塞”)。这些额外缓冲的帧只能逐个依次呈现,导致额外延迟。这种情况还可能导致应用无可用缓冲区而进入阻塞等待状态。尽管应用实际工作耗时仍在截止时间内,但由于队列填满,所有帧至少会延迟一个垂直同步周期呈现。此状态下帧仍可能流畅,但输入延迟会因延迟呈现而增加。

SurfaceFlinger 卡顿

SurfaceFlinger 合成帧的两种方式:

  • 设备合成:使用专用硬件
  • GPU/客户端合成:使用 GPU 合成

需注意:设备合成在主线程上以阻塞调用方式执行;而 GPU 合成则并行进行。SurfaceFlinger 执行必要的绘制调用后,将 GPU 围栏传递给显示设备,后者等待围栏信号触发后再呈现帧。

  • SurfaceFlingerCpuDeadlineMissed
    SurfaceFlinger 需在给定截止时间内完成工作。若主线程运行超时,则判定为该类型卡顿。SurfaceFlinger 的 CPU 时间指主线程耗时:若使用设备合成,包括整个合成时间;若使用 GPU 合成,包括绘制调用写入时间及将帧交给 GPU 的时间。

  • SurfaceFlingerGpuDeadlineMissed
    SurfaceFlinger 主线程 CPU 时间与 GPU 合成时间总和超过预期。此时 CPU 时间可能仍在截止时间内,但由于 GPU 工作未按时完成,帧被推迟至下一垂直同步周期呈现。

  • DisplayHAL
    指 SurfaceFlinger 按时完成工作并将帧发送至 HAL(硬件抽象层),但帧未在目标垂直同步周期呈现,而是延迟至下一周期。可能是 SurfaceFlinger 未给 HAL 预留足够时间,或 HAL 自身存在真实延迟。

  • PredictionError
    SurfaceFlinger 调度器会提前规划帧的呈现时间,但该预测有时会与实际硬件垂直同步时间产生偏差。例如:某帧预测呈现时间为 20ms,因估计漂移导致实际呈现时间为 23ms,此类情况称为调度器的“预测误差”。调度器会定期自我校正,因此这种漂移并非永久性。不过,存在预测漂移的帧仍会被归类为卡顿以用于跟踪。

    孤立的预测误差通常不会被用户察觉,因为调度器能快速适应并修正偏差。

Unknown Jank

顾名思义,此类卡顿原因未知。例如:SurfaceFlinger 或应用运行超时错过截止时间,但帧仍提前呈现。这种情况发生概率极低,但并非完全不可能。

posted @ 2025-07-01 21:34  yooooooo  阅读(30)  评论(0)    收藏  举报