Flutter笔记:关于SchedulerBinding

简介: Flutter笔记:关于SchedulerBinding

1. 概述

在Flutter框架中,SchedulerBinding是一个非常重要的mixin,它为Flutter应用程序提供了调度帧(Frame)和任务(Task)的核心功能。SchedulerBinding与Flutter引擎紧密配合,负责管理帧的生命周期,协调各种类型的回调函数,以及优化应用的性能。

1.1 Flutter框架中SchedulerBinding发挥的作用

SchedulerBinding是Flutter框架中不可或缺的一部分,它为开发者提供了控制应用生命周期、优化性能的强大工具。SchedulerBinding在Flutter框架中的的功能包括:


它是连接Flutter应用程序和Flutter引擎的桥梁,负责与引擎进行通信,接收引擎发出的帧事件(Frame events),并触发相应的回调函数。

它管理着Flutter应用的帧率(Frame rate),根据设备性能和应用需求,自动调整帧率以提供流畅的用户体验。

它提供了注册和调度各种类型回调函数的API,包括Transient frame callbacks、Persistent frame callbacks和Post-frame callbacks等,让开发者能够在适当的时机执行自己的逻辑。

它还负责管理任务队列,根据任务的优先级和当前的调度策略,合理地安排任务的执行顺序,避免应用出现卡顿或无响应的情况。

1.2 SchedulerBinding的主要职责

SchedulerBinding的主要职责可以归纳为以下几点:


帧调度(Frame scheduling):与Flutter引擎协作,接收并处理onBeginFrame和onDrawFrame回调,触发帧的构建(build)和绘制(draw)过程。

回调管理(Callback management):提供注册和移除Transient frame callbacks、Persistent frame callbacks和Post-frame callbacks的方法,确保回调函数在正确的时机被调用。

任务调度(Task scheduling):管理一个优先级队列,根据任务的优先级和当前的调度策略(schedulingStrategy),合理安排任务的执行顺序。

帧率控制(Frame rate control):根据设备性能和应用需求,动态调整帧率(timeDilation),以平衡流畅度和电池消耗。

生命周期管理(Lifecycle management):监听应用的生命周期事件(AppLifecycleState),如应用进入前台或后台,并触发相应的回调函数。

时间戳处理(Timestamp handling):提供currentFrameTimeStamp和currentSystemFrameTimeStamp属性,供开发者获取当前帧的时间戳,同时还支持添加自定义的时间戳回调(TimestampCallback)。

通过对这些职责的有效履行,SchedulerBinding确保了Flutter应用能够高效、流畅地运行,并为开发者提供了灵活的控制手段。在后续的章节中,我们将深入探讨SchedulerBinding的关键概念和使用方法。


需要指出,SchedulerBinding是一种非常低层次、高风险的操作,只应该在一些非常特殊和罕见的场景下使用。在绝大多数情况下,我们都应该使用Flutter提供的高层API、组件和插件,以保证应用的性能、稳定性和可维护性。只有在我们确实需要实现一些Flutter框架不支持的、非常定制化的功能时,才应该考虑直接使用SchedulerBinding,并且要非常谨慎地进行设计、实现和测试。

2. SchedulerBinding的关键概念

要理解SchedulerBinding的工作原理和使用方法,首先需要了解一些关键概念。本节将介绍FrameFrame callbacks以及SchedulerBinding中的几种callback类型和调度阶段。

2.1 Frame(帧)和Frame callbacks(帧回调)

Flutter中,Frame表示一次完整的UI渲染过程,包括构建Widget树、布局、绘制等步骤。Frame的生成由Flutter引擎驱动,通常以每秒60次的频率进行。


而Frame callbacks则是在每一帧的特定时间点被调用的回调函数。通过Frame callbacks,我们可以在一帧的不同阶段执行自定义的逻辑,例如启动动画、更新状态等。SchedulerBinding提供了注册和管理Frame callbacks的方法。


2.2 Transient frame callbacks(瞬时帧回调)

Transient frame callbacks是一次性的帧回调,只在注册后的下一帧被调用一次。可以通过SchedulerBinding.scheduleFrameCallback方法注册Transient frame callback

SchedulerBinding.instance.scheduleFrameCallback((_) {
  // 在下一帧执行一些操作
  print('Transient frame callback executed');
});

Transient callbacks适用于只需要执行一次的场景,例如触发一个动画。

2.3 Persistent frame callbacks(持久帧回调)

Transient frame callbacks不同,Persistent frame callbacks会在每一帧都被调用,直到被移除为止。可以通过SchedulerBinding.addPersistentFrameCallback方法注册:

void onFrame(Duration timeStamp) {
  // 在每一帧执行一些操作
  print('Persistent frame callback executed');
}

SchedulerBinding.instance.addPersistentFrameCallback(onFrame);

要移除一个Persistent frame callback,可以调用SchedulerBinding.removePersistentFrameCallback方法:

SchedulerBinding.instance.removePersistentFrameCallback(onFrame);

Persistent callbacks通常用于需要在每一帧都执行的场景,例如实时更新UI、动画等。

2.4 Post-frame callbacks(后帧回调)

Post-frame callbacks会在当前帧的所有Persistent callbacks执行完毕后、下一帧开始前被调用。可以通过SchedulerBinding.addPostFrameCallback方法注册:

SchedulerBinding.instance.addPostFrameCallback((_) {
  // 在当前帧结束后执行一些操作  
  print('Post-frame callback executed');
});

Post-frame callbacks适用于需要在当前帧的最后执行的场景,例如在布局或绘制完成后更新状态。

2.5 Scheduler phases(调度阶段)

SchedulerBinding将一帧划分为几个阶段,不同类型的callbacks在不同阶段执行:

  1. Idle(空闲): 表示当前没有正在处理的帧。
  2. Transient callbacks(瞬时回调): 执行Transient frame callbacks
  3. Midframe microtasks(帧中微任务): 执行Transient callbacks产生的微任务。
  4. Persistent callbacks(持久回调): 执行Persistent frame callbacks
  5. Post-frame callbacks(后帧回调): 执行Post-frame callbacks

了解这些阶段有助于了解这些阶段有助于我们选择合适的callback类型,并避免在错误的阶段执行耗时操作而影响UI性能。例如,在Persistent callbacks阶段执行复杂的计算或I/O操作可能会导致掉帧。


通过SchedulerBinding.schedulerPhase属性可以获取当前的调度阶段:

SchedulerPhase phase = SchedulerBinding.instance.schedulerPhase;
if (phase == SchedulerPhase.persistentCallbacks) {
  // 在持久回调阶段执行一些操作
} else if (phase == SchedulerPhase.idle) {
  // 在空闲阶段执行一些操作
}

理解Frame、Frame callbacks以及SchedulerBinding的调度阶段,是高效使用SchedulerBinding的基础。在实际应用中,我们应该根据具体的需求选择合适的callback类型和调用时机,避免不必要的性能开销,从而构建流畅、响应迅速的Flutter应用。


3. SchedulerBinding中的回调

3.1 注册和移除Transient frame callbacks

Transient frame callbacks 是一次性的帧回调,会在下一帧开始时被调用,调用后就会自动移除。我们可以通过 SchedulerBinding.instance.scheduleFrameCallback 方法来注册一个 Transient frame callback

int id = SchedulerBinding.instance.scheduleFrameCallback((_) {
  // 在下一帧开始时执行一些操作
  ...
}, rescheduling: false);

其中 rescheduling 参数表示是否在回调执行完后自动重新注册,一般不需要。


如果要提前移除一个 Transient frame callback,可以使用 SchedulerBinding.instance.cancelFrameCallbackWithId 方法:

SchedulerBinding.instance.cancelFrameCallbackWithId(id);

3.2 注册Persistent frame callbacks

Persistent frame callbacks 是持久的帧回调,一旦注册后,每一帧都会被调用,直到主动移除。我

们可以通过 SchedulerBinding.instance.addPersistentFrameCallback 方法来注册:

void callback(Duration timeStamp) {
  // 在每一帧执行一些操作
  ...
}
SchedulerBinding.instance.addPersistentFrameCallback(callback);

如果要移除一个 Persistent frame callback,可以使用 SchedulerBinding.instance.removePersistentFrameCallback 方法:

SchedulerBinding.instance.removePersistentFrameCallback(callback);

3.3 注册Post-frame callbacks

Post-frame callbacks 是一次性的后帧回调,会在当前帧绘制完成后被调用。我们可以通过 SchedulerBinding.instance.addPostFrameCallback 方法来注册:

SchedulerBinding.instance.addPostFrameCallback((_) {
  // 在当前帧绘制完成后执行一些操作  
  ...
});

Post-frame callbacks 不需要主动移除,在回调函数执行完后会自动移除。

3.4 调度和执行任务(Task)

除了帧回调,SchedulerBinding 还提供了调度和执行任务的能力。我们可以通过

SchedulerBinding.instance.scheduleTask 方法来调度一个异步任务:

SchedulerBinding.instance.scheduleTask(() async {
  // 执行一些异步任务
  ...  
}, Priority.animation);

其中第二个参数是任务的优先级,优先级越高的任务会越早执行。


SchedulerBinding 内部有一个任务队列,任务会在每一帧的空闲时间去执行。如果某一帧有动画回调,则优先级低于 Priority.animation 的任务会暂停执行,让出时间给动画,避免动画卡顿。


以上就是 SchedulerBinding 的一些主要功能和用法,它为 Flutter 应用提供了灵活的调度能力。合理利用 SchedulerBinding,可以实现各种自定义的动画效果、与外部数据源同步、优化性能等功能,是 Flutter 开发中非常重要的一个类。

3.5 控制帧率(Frame rate)

Flutter默认的帧率是60fps,但有时我们可能需要降低帧率以节省电量,或者提高帧率以获得更流畅的动画效果。SchedulerBinding提供了timeDilation属性来控制帧率:

// 降低帧率到30fps
SchedulerBinding.instance.timeDilation = 2.0;

// 提高帧率到120fps
SchedulerBinding.instance.timeDilation = 0.5;


timeDilation的默认值为1.0,表示60fps。设置为2.0时,每一帧的时间会变为原来的2倍,所以帧率降为30fps。设置为0.5时,每一帧的时间会变为原来的一半,所以帧率提高为120fps。


注意,提高帧率会增加CPU和GPU的负载,可能会导致耗电增加和发热加剧,所以一般只在必要时短暂地提高帧

3.6 监听应用生命周期事件

SchedulerBinding提供了一些属性和方法来监听应用的生命周期事件:

  • lifecycleState属性:表示应用当前的生命周期状态,是一个AppLifecycleState枚举值。
  • addObserverremoveObserver方法:可以添加和移除SchedulerObserver,在应用生命周期状态发生变化时会收到通知。

例如:

class MySchedulerObserver implements SchedulerObserver {
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('App lifecycle state changed to $state');
  }
}

MySchedulerObserver observer = MySchedulerObserver();
SchedulerBinding.instance.addObserver(observer);

上面的代码添加了一个SchedulerObserver,它会在应用生命周期状态发生变化时打印出新的状态。

3.7 添加和移除时间戳回调(TimestampCallback)

SchedulerBinding允许我们添加时间戳回调函数,在每一帧开始时,Flutter引擎会将该帧的时间戳信息传递给这些回调函数。这对于实现一些需要精确计时的功能非常有用,比如音视频同步、数据采集等。

可以通过SchedulerBinding.instance.addTimingsCallback方法添加时间戳回调:

void callback(List<FrameTiming> timings) {
  // 处理时间戳信息
  ...
}
SchedulerBinding.instance.addTimingsCallback(callback);
目录
相关文章
|
3月前
|
Dart
Flutter笔记:手动配置VSCode中Dart代码自动格式化
Flutter笔记:手动配置VSCode中Dart代码自动格式化
467 5
|
3月前
|
开发者 Windows
Flutter笔记:Widgets Easier组件库(9)使用弹窗
Flutter笔记:Widgets Easier组件库(9)使用弹窗
110 3
|
3月前
|
数据安全/隐私保护 Android开发 开发者
Flutter笔记:Widgets Easier组件库-使用隐私守卫
Flutter笔记:Widgets Easier组件库-使用隐私守卫
51 2
|
3月前
|
UED 开发者
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
62 2
|
3月前
|
开发者
Flutter笔记:Widgets Easier组件库(11)- 使用提示吐丝(Tip Toasts)
Flutter笔记:Widgets Easier组件库(11)- 使用提示吐丝(Tip Toasts)
51 1
|
3月前
|
开发者
Flutter笔记:Widgets Easier组件库 - 使用标签(Tag)
Flutter笔记:Widgets Easier组件库 - 使用标签(Tag)
111 0
|
3月前
|
JSON Android开发 数据格式
Flutter笔记:美工设计.导出视频到RIVE
Flutter笔记:美工设计.导出视频到RIVE
58 0
|
3月前
|
开发者
Flutter笔记:Widgets Easier组件库(12)使用消息吐丝(Notify Toasts)
Flutter笔记:Widgets Easier组件库(12)使用消息吐丝(Notify Toasts)
75 0
|
3月前
|
数据安全/隐私保护 UED 开发者
Flutter笔记:Widgets Easier组件库(10)快速处理承若型对话
Flutter笔记:Widgets Easier组件库(10)快速处理承若型对话
35 0
|
1月前
|
Android开发 iOS开发 容器
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
鸿蒙harmonyos next flutter混合开发之开发FFI plugin