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的工作原理和使用方法,首先需要了解一些关键概念。本节将介绍Frame、Frame 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
在不同阶段执行:
- Idle(空闲): 表示当前没有正在处理的帧。
- Transient callbacks(瞬时回调): 执行Transient frame callbacks。
- Midframe microtasks(帧中微任务): 执行Transient callbacks产生的微任务。
- Persistent callbacks(持久回调): 执行Persistent frame callbacks。
- 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枚举值。addObserver
和removeObserver
方法:可以添加和移除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);