SchedulerBinding原理

SchedulerBinding 是 Flutter 框架中负责协调帧调度的核心组件,它管理着应用程序的渲染时机和各种回调的执行顺序。下面我将详细分析 SchedulerBinding 的调度原理和状态切换机制。

核心状态:SchedulerPhase

SchedulerBinding 通过 SchedulerPhase 枚举定义了调度过程中的不同阶段:

arduino 复制代码
enum SchedulerPhase {
  idle,                // 空闲状态,没有正在处理的帧
  transientCallbacks,  // 处理临时回调(如动画)
  midFrameMicrotasks,  // 处理微任务
  persistentCallbacks, // 处理持久回调(如布局和绘制)
  postFrameCallbacks,  // 处理帧后回调
}

这些状态按顺序切换,代表了一帧处理的完整生命周期。

调度原理

1. 帧请求机制

SchedulerBinding 通过以下方法请求新帧:

javascript 复制代码
void scheduleFrame() {
  if (_hasScheduledFrame || !_framesEnabled)
    return;
  ensureVisualUpdate();
}

void ensureVisualUpdate() {
  if (_hasScheduledFrame)
    return;
  _hasScheduledFrame = true;
  window.scheduleFrame();
}

当调用 scheduleFrame() 时,SchedulerBinding 会通过 window.scheduleFrame() 向底层引擎请求在下一个 VSync 信号到来时开始一个新帧。

2. 回调注册系统

SchedulerBinding 维护了三种主要的回调列表:

1.临时帧回调(Transient Frame Callbacks)

dart 复制代码
final Map<int, _FrameCallbackEntry> _transientCallbacks = <int, _FrameCallbackEntry>{};

用于动画等需要在特定帧执行一次的回调。

2.持久帧回调(Persistent Frame Callbacks)

ini 复制代码
final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];

用于布局和绘制等每帧都需要执行的回调。

3.帧后回调(Post-Frame Callbacks)

ini 复制代码
final List<FrameCallback> _postFrameCallbacks = <FrameCallback>[];

用于在当前帧完成后执行的回调

3. 帧处理流程

当 VSync 信号到来时,Flutter 引擎会调用 SchedulerBinding 的两个关键方法:

3.1 handleBeginFrame
ini 复制代码
void handleBeginFrame(Duration rawTimeStamp) {
  _hasScheduledFrame = false;
  _currentFrameTimeStamp = rawTimeStamp;
  
  // 处理临时回调
  _schedulerPhase = SchedulerPhase.transientCallbacks;
  final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
  _transientCallbacks = <int, _FrameCallbackEntry>{};
  callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
    if (!_removedIds.contains(id))
      _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
  });
  _removedIds.clear();
  
  // 切换到微任务阶段
  _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}

这个方法处理帧开始时的工作,主要执行临时回调(如 Ticker 的回调)。

3.2 handleDrawFrame
ini 复制代码
void handleDrawFrame() {
  // 处理持久回调
  _schedulerPhase = SchedulerPhase.persistentCallbacks;
  for (final FrameCallback callback in _persistentCallbacks)
    _invokeFrameCallback(callback, _currentFrameTimeStamp!);
  
  // 处理帧后回调
  _schedulerPhase = SchedulerPhase.postFrameCallbacks;
  final List<FrameCallback> localPostFrameCallbacks = 
      List<FrameCallback>.from(_postFrameCallbacks);
  _postFrameCallbacks.clear();
  for (final FrameCallback callback in localPostFrameCallbacks)
    _invokeFrameCallback(callback, _currentFrameTimeStamp!);
  
  // 返回空闲状态
  _schedulerPhase = SchedulerPhase.idle;
  _currentFrameTimeStamp = null;
}

这个方法处理帧的绘制工作,执行持久回调和帧后回调。

4. 性能模式管理

SchedulerBinding 还支持不同的性能模式,通过 requestPerformanceMode 方法实现:

ini 复制代码
PerformanceModeRequestHandle? requestPerformanceMode(DartPerformanceMode mode) {
  // 冲突请求不允许
  if (_performanceMode != null && _performanceMode != mode) {
    return null;
  }

  if (_performanceMode == mode) {
    assert(_numPerformanceModeRequests > 0);
    _numPerformanceModeRequests++;
  } else if (_performanceMode == null) {
    assert(_numPerformanceModeRequests == 0);
    _performanceMode = mode;
    _numPerformanceModeRequests = 1;
  }

  return PerformanceModeRequestHandle._(_disposePerformanceModeRequest);
}

这允许应用程序请求特定的性能模式(如 latency 或 throughput),以优化特定场景下的性能。

状态切换机制

SchedulerBinding 的状态切换遵循严格的顺序,通过 _schedulerPhase 字段跟踪当前阶段:

  1. idle → transientCallbacks :当 handleBeginFrame 被调用时
  2. transientCallbacks → midFrameMicrotasks:当所有临时回调执行完毕时
  3. midFrameMicrotasks → persistentCallbacks :当 handleDrawFrame 被调用时
  4. persistentCallbacks → postFrameCallbacks:当所有持久回调执行完毕时
  5. postFrameCallbacks → idle:当所有帧后回调执行完毕时

这种状态机设计确保了回调的执行顺序符合预期,例如:

  • 动画更新(transientCallbacks)在布局和绘制(persistentCallbacks)之前执行
  • 布局和绘制完成后才执行帧后回调(postFrameCallbacks

生命周期状态管理

除了帧内的状态切换,SchedulerBinding 还管理应用程序的生命周期状态:

ini 复制代码
void handleAppLifecycleStateChanged(AppLifecycleState state) {
  _lifecycleState = state;
  switch (state) {
    case AppLifecycleState.resumed:
    case AppLifecycleState.inactive:
      _framesEnabled = true;
      break;
    case AppLifecycleState.paused:
    case AppLifecycleState.detached:
      _framesEnabled = false;
      break;
  }
}

当应用进入后台(paused)或分离(detached)状态时,SchedulerBinding 会禁用帧调度(_framesEnabled = false),避免不必要的计算和电池消耗。

时间膨胀(Time Dilation)

SchedulerBinding 支持时间膨胀功能,可以放慢动画速度以便调试:

ini 复制代码
double get timeDilation => _timeDilation;
double _timeDilation = 1.0;
set timeDilation(double value) {
  assert(value > 0.0);
  if (_timeDilation == value)
    return;
  _timeDilation = value;
  if (_currentFrameTimeStamp != null) {
    _adjustForTimeDilation();
  }
}

通过调整时间戳,可以实现动画的慢动作效果,这在调试复杂动画时非常有用。

优先级管理

SchedulerBinding 通过 Priority 类管理任务的优先级:

arduino 复制代码
class Priority {
  static const int animation = 100000;
  static const int touch = 200000;
  // ...
}

这些优先级值用于确定任务的执行顺序,特别是在使用 scheduleTask 方法时。

总结

SchedulerBinding 通过精心设计的状态机和回调系统,实现了 Flutter 应用程序的高效帧调度:

  1. 状态驱动:通过 SchedulerPhase 明确定义帧处理的各个阶段
  2. 回调分类:将回调分为临时、持久和帧后三类,确保正确的执行顺序
  3. 生命周期感知:根据应用程序的生命周期状态调整帧调度行为
  4. 性能优化:支持不同的性能模式和任务优先级

这种设计使得 Flutter 能够在保持高性能的同时,提供流畅的用户体验和灵活的开发模型。理解 SchedulerBinding 的调度原理和状态切换机制,对于开发高性能的 Flutter 应用和解决性能问题至关重要。

Flutter 中的持久回调(Persistent Callbacks)详解

持久回调的定义

在 Flutter 的 SchedulerBinding 中,"持久回调"(Persistent Callbacks)是指那些需要在每一帧都执行的回调函数。与临时回调(Transient Callbacks)不同,持久回调一旦注册,就会在每一帧渲染时被调用,直到应用程序结束或者这些回调被明确移除。

持久的时间范围

持久回调的"持久"并不是指某个特定的时间长度,而是指它们的生命周期特性:

  1. 跨帧持久:一旦注册,持久回调会在每一帧都被调用,而不是像临时回调那样只在特定帧执行一次。

  2. 应用生命周期:持久回调通常与特定子系统(如渲染管道)的生命周期绑定,可能持续整个应用程序的生命周期。

  3. 直到明确移除 :持久回调会一直存在,直到通过 removeFrameCallback 方法明确移除。

持久回调的作用

持久回调在 Flutter 框架中扮演着关键角色:

  1. 驱动渲染管道:最重要的用途是驱动 Flutter 的渲染管道,确保视图在每一帧都得到正确更新。

  2. 布局和绘制:负责执行布局计算和实际绘制操作,将虚拟视图树转换为屏幕上的像素。

  3. 保持视图一致性:确保用户界面与应用程序状态保持同步,反映最新的数据和交互状态。

  4. 处理系统级更新:响应系统级事件(如屏幕旋转、键盘显示/隐藏等),更新界面布局。

持久回调的应用场景

1. 渲染引擎

最主要的应用场景是在 RendererBinding 中,它注册了一个持久回调来驱动整个渲染管道:

scss 复制代码
void initInstances() {
  super.initInstances();
  _instance = this;
  
  // 注册持久回调来驱动渲染管道
  addPersistentFrameCallback(_handlePersistentFrameCallback);
}

void _handlePersistentFrameCallback(Duration timeStamp) {
  drawFrame();  // 执行实际的绘制工作
}

这个持久回调确保每帧都会触发渲染管道的执行,包括布局、绘制和合成等步骤。

2. 小部件绑定

WidgetsBinding 使用持久回调来管理小部件树的构建和更新:

scss 复制代码
void drawFrame() {
  buildOwner!.buildScope(renderViewElement!);  // 构建小部件
  super.drawFrame();  // 执行渲染
  buildOwner!.finalizeTree();  // 清理
}

3. 自定义渲染组件

开发者可以创建自定义的渲染组件,这些组件可能需要在每帧更新:

scala 复制代码
class CustomRenderObject extends RenderObject {
  CustomRenderObject() {
    SchedulerBinding.instance.addPersistentFrameCallback(_update);
  }
  
  void _update(Duration timeStamp) {
    // 执行每帧所需的更新
    markNeedsPaint();
  }
  
  @override
  void dispose() {
    SchedulerBinding.instance.removeFrameCallback(_update);
    super.dispose();
  }
}

4. 性能监控系统

Flutter 的性能监控工具使用持久回调来收集每帧的性能数据:

scss 复制代码
void initPerformanceMonitoring() {
  SchedulerBinding.instance.addPersistentFrameCallback((Duration timeStamp) {
    // 记录帧开始时间
    _frameStartTime = DateTime.now();
  });
  
  // 在帧后回调中记录帧结束时间
  SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
    final Duration frameDuration = DateTime.now().difference(_frameStartTime);
    _recordFrameMetrics(frameDuration);
  });
}

总结

持久回调是 Flutter 框架中确保视图持续更新的关键机制。它们:

  1. 在每一帧都会执行,而不是一次性的
  2. 主要用于驱动渲染管道和布局系统
  3. 在临时回调(如动画)执行后运行,确保动画状态能够正确反映在屏幕上
  4. 与应用程序或特定子系统的生命周期绑定,而不是特定时间长度

理解持久回调的工作原理,对于深入掌握 Flutter 的渲染机制和开发高性能应用非常重要。特别是在需要自定义渲染行为或优化渲染性能时,了解持久回调的角色和执行时机可以帮助开发者做出更明智的设计决策。

相关推荐
李新_9 分钟前
我们封装了哪些好用的Flutter Mixin
android·flutter
leluckys21 分钟前
flutter 专题 六十三 Flutter入门与实战作者:xiangzhihong8Fluter 应用调试
前端·javascript·flutter
又菜又爱coding23 分钟前
Flutter异常Couldn‘t find dynamic library in default locations
flutter
帅次23 分钟前
Flutter Expanded 与 Flexible 详解
android·flutter·ios·小程序·webview
衿璃6 小时前
flutter 路由跳转动画设置
android·flutter
Nicholas686 小时前
SchedulerBinding源码解析
flutter
Ya-Jun6 小时前
环境搭建与入门:Flutter SDK安装与配置
flutter
sean9086 小时前
使用 Flutter 遇坑小计
flutter·gradle·卡住·没反应
Ya-Jun16 小时前
状态管理最佳实践:Bloc架构实践
flutter