SchedulerBinding 的三个Frame回调

addPersistentFrameCallbackaddPostFrameCallbackaddTimingsCallback 确实都是 Flutter 框架调度中"Frame 级别"的回调,但三者触发时机完全不同, 它们分别连接着 Frame 生命周期的三个阶段。 一个详细、专业但清晰的对照和时间线讲解 👇


🧭 一、概览表

回调接口 注册位置 执行时机(帧流程阶段) 回调线程 常见用途 是否每帧持续触发
addPersistentFrameCallback SchedulerBinding 在每帧开始时,绘制流程开始之前(布局、绘制前) UI Thread(Dart isolate) 自定义动画、同步逻辑、监控 frame build ✅ 是,每帧都会触发
addPostFrameCallback WidgetsBinding 帧绘制完成后、但在下一帧开始前(UI 树渲染结束) UI Thread(Dart isolate) 在 build 完后执行一次性逻辑(如状态恢复、测量布局等) ❌ 否,只执行一次
addTimingsCallback SchedulerBinding 一帧真正完成后(包括 rasterizer 渲染、GPU 提交) Engine ➜ Dart isolate 性能分析、帧率统计、掉帧检测 ⚙️ 仅在帧真正渲染完成时触发(由 Engine 主动回调)

🧩 二、完整帧生命周期时间线图

下面是一帧从触发到结束的时间轴:

scss 复制代码
┌──────────────────────────────────────────────────────────────┐
│ 一帧的完整生命周期(Flutter Pipeline)                       │
├──────────────────────────────────────────────────────────────┤
│ vsync 信号到达 → 触发 Scheduler.handleBeginFrame             │
│      ↓                                                       │
│ ① addPersistentFrameCallback() 触发                          │ ← 每帧都会调用(UI逻辑)
│      ↓                                                       │
│ Widgets build/rebuild/layout/paint 完成                      │
│      ↓                                                       │
│ ② addPostFrameCallback() 执行                                │ ← 本帧后执行一次(UI在屏幕前执行的最后逻辑)
│      ↓                                                       │
│ UI 层命令发送给 Engine,Rasterizer 开始渲染 → GPU            │
│      ↓                                                       │
│ ③ addTimingsCallback() 触发                                 │ ← Engine 通知 Dart:此帧渲染周期完结
│      ↓                                                       │
│ 下一帧 vsync 周期开始                                         │
└──────────────────────────────────────────────────────────────┘

🧠 三、详细解释

1️⃣ addPersistentFrameCallback

  • 🔹 每当 Flutter engine 安排一次 Frame(vsync 信号触发)时调用;
  • 🔹 在 WidgetsBinding.drawFrame() 之前;
  • 🔹 多用于做动画同步,如 Ticker, AnimationController
  • 🔹 不可在里面直接调用 setState()(会导致无限循环)。
dart 复制代码
SchedulerBinding.instance.addPersistentFrameCallback((Duration timeStamp) {
  // 每帧都会执行一次
  // timeStamp:距离应用启动的时间
});

用途:开发 Frame 驱动逻辑、动画系统、心跳检测。


2️⃣ addPostFrameCallback

  • 🔹 注册一个下一帧绘制完成后执行的回调;
  • 🔹 只执行一次;
  • 🔹 常用于需要在 widget build 完之后去访问 context 或做布局调整。
dart 复制代码
WidgetsBinding.instance.addPostFrameCallback((_) {
  print("这一帧的Widget都build & render完毕之后执行");
});

用途:执行一次性的渲染后逻辑(比如弹框、滚动控制、布局测量)。


3️⃣ addTimingsCallback

  • 🔹 由 Flutter 引擎 在一帧完全渲染完成(包括 GPU 渲染)后回调;
  • 🔹 参数提供本帧的渲染性能信息:
    • build、rasterize 时间、
    • start/finish 时间戳;
  • 🔹 不一定每帧都有(只有当一帧真正提交给屏幕后才会触发)。
dart 复制代码
SchedulerBinding.instance.addTimingsCallback((List<FrameTiming> timings) {
  for (final t in timings) {
    print('build: ${t.buildDuration}, raster: ${t.rasterDuration}');
  }
});

用途:性能监控、丢帧检测、UI心跳。


🧩 四、对应的 Pipeline Phase(官方描述)

Scheduler Phase 回调接口 官方解释
TRANSIENT_CALLBACKS addPersistentFrameCallback 一帧准备阶段,执行持久 frame 回调
POST_FRAME_CALLBACKS addPostFrameCallback 当前帧绘制完毕后执行延迟回调
FrameTimings (engine reported) addTimingsCallback 一帧渲染完成,engine 向 Dart 报告

⚙️ 五、典型使用示例汇总

dart 复制代码
void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // 每帧都会触发 - 可作心跳
  SchedulerBinding.instance.addPersistentFrameCallback((timeStamp) {
    print('Persistent at ${timeStamp.inMilliseconds}');
  });

  // 当前帧绘制完后执行一次
  WidgetsBinding.instance.addPostFrameCallback((_) {
    print('Widget build done.');
  });

  // 渲染完成后(含GPU提交)
  SchedulerBinding.instance.addTimingsCallback((timings) {
    final t = timings.last;
    print('Frame done. build=${t.buildDuration}, raster=${t.rasterDuration}');
  });

  runApp(MyApp());
}

✅ 最终总结表

回调 调用频率 触发时机 所属线程 典型用途
addPersistentFrameCallback 每帧 每帧开始(UI build 前) UI isolate 动画驱动、持续逻辑、心跳
addPostFrameCallback 一次 当前帧 build 后(UI 树构建完) UI isolate 布局调整、逻辑收尾
addTimingsCallback 条件触发 一帧 GPU 渲染完成后 Engine 调用 Dart 性能分析、渲染监控

🔥 一句总结记忆法:

Persistent ------ 每帧都来; Post ------ 帧后执行一次; Timings ------ 一帧真上屏后报告性能。


相关推荐
AAA简单玩转程序设计22 分钟前
C++进阶小技巧:让代码从"能用"变"优雅"
前端·c++
子洋22 分钟前
群晖 DSM 更新后 Cloudflare DDNS 失效的排查记录
前端·后端·dns
一颗烂土豆30 分钟前
告别 Vue 多分辨率适配烦恼:vfit 让元素定位 “丝滑” 跨设备
前端·vue.js
ArkPppp32 分钟前
牛刀小试:Vue 3的响应式系统和Proxy?
前端
天天开发34 分钟前
Flutter每日库: local_auth本地设备验证插件
flutter
venton34 分钟前
Next.js SSR 实战:从零到一,构建服务端渲染应用
前端
萌狼蓝天41 分钟前
[Vue]性能优化:动态首行与动态列的匹配,表格数据格式处理性能优化
前端·javascript·vue.js·性能优化·ecmascript
一 乐44 分钟前
宠物管理宠物医院管理|基于Java+vue的宠物医院管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·宠物
一 乐1 小时前
学习辅导系统|数学辅导小程序|基于java+小程序的数学辅导小程序设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习·小程序