flutter scheduler 机制
flutter 中的 WidgetsFlutterBinding 集成了 GestureBinding、ServicesBinding、SchedulerBinding、PaintingBinding、SemanticsBinding、RendererBinding、WidgetsBinding 等 7 种 Binding,它们都有自己在功能上的划分,其中,SchedulerBinding 主要负责的是 flutter 中视图刷新、延迟回调相关的,有点类似于 android 中的 Choreographer 的功能,事实上 SchedulerBinding 的一部分功能也是基于 Choreographer 完成的。
void initInstances() {
super.initInstances();
_instance = this;
window.onBeginFrame = _handleBeginFrame;
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow();
}
在 SchedulerBinding 的 initInstances 中,向 window 注册了 onBeginFrame 和 onDrawFrame 两个函数,然后给 SystemChannels.lifecycle 注册了一个接收器,用于接收生命周期相关的消息,这个主要是用于控制 flutter 是否需要继续刷新视图,最后的 readInitialLifecycleStateFromNativeWindow 能够从 native 层读取到当前的生命周期状态,最为 flutter 的初始状态。
SchedulerBinding 本身大致包含两部分功能,第一步部分为刷新视图相关的,当需要刷新视图时,就会调用 scheduleFrame 向 native 层发起一个刷新视图的请求,然后当时机到达时,native 通过调用 onBegineFrame 和 onDrawFrame 这两个函数,SchedulerBinding 便会在这两个回调用完成刷新视图所需的操作,比如更新 widgets、执行动画、直到最后视图的渲染,以上操作都完成后,SchedulerBinding 便会再调用 scheduleFrame 发起刷新请求,等待下一次刷新时机的到来。同时 SchedulerBinding 也支持在视图刷新的间隙中执行一些其他操作,可以通过 scheduleTask 传入待执行的 task ,SchedulerBinding 会寻找合适的时机去执行,但是本质上这些 task 还是运行在主线程中,与 Future 类似,任务过重会导致 UI 卡顿,只不过这里的执行更严格,增加了对 task 优先级的区分。
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
如上,在 flutter 应用启动的最后阶段,调用了 scheduleWarmUpFrame,这个函数就是启动 SchedulerBinding 进行第一次试图更新的,在它的内部依次会调用 handleBeginFrame、handleDrawFrame 和 scheduleFrame 三个函数,完成第一次渲染的工作。
handleBegineFrame
void handleBeginFrame(Duration rawTimeStamp) {
Timeline.startSync('Frame', arguments: timelineWhitelistArguments);
_firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null)
_lastRawTimeStamp = rawTimeStamp;
assert(schedulerPhase == SchedulerPhase.idle);
_hasScheduledFrame = false;
try {
// TRANSIENT FRAME CALLBACKS
Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
_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();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
从 handleBeginFrame 的实现来看,这里只是完成了所有 transientCallbacks 回调,这个回调通过 scheduleFrameCallback 函数添加,从这个函数的使用上看,在 Ticker.scheduleTick 中,它向 transientCallbacks 中添加了 tick 回调函数,具体使用这里不阐述,而 Ticker 主要的功能,就是被 AnimationController 使用,也就是动画,所以,transientCallbacks 主要的功能可以看作为用于动画的实现。
那么也就可以理解,为什么 transientCallbacks 会在 onDrawFrame 之前执行了,在渲染之前先更新各 widgets ,然后再将 widgets 渲染。
handleDrawFrame
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
_currentFrameTimeStamp = null;
}
}
在 handleDrawFrame 中执行了两种回调函数,persistentCallbacks 和 postFrameCallbacks。
persistentCallbacks
persistentCallbacks 也就是固定的回调,不会每次执行之后被清空,而这个回调在 flutter 中目前只有两个使用场景,一个是在 WidgetInspectorService 中用于统计渲染过程的耗时,另一个就是在 RenderBinding 中,添加了 _handlePersistentFrameCallback 回调,这个函数被用于在 RenderBinding 中开启画面渲染。
所以 persistentCallbacks 可以看作是 flutter 中真正的渲染过程,这个回调执行完了之后,用户看到的视图就会刷新。
postFrameCallbacks
postFrameCallbacks 在渲染之后调用,可用于执行一些清理操作或者是继续请求刷新视图等。比如在 WidgetInspectorService,通过添加 postFrameCallbacks 统计渲染的耗时。
scheduleFrame
scheduleFrame 中调用了 window.scheduleFrame,内部会向 native 请求回调,它对应的 engine 中的函数为:
void ScheduleFrame(Dart_NativeArguments args) {
UIDartState::Current()->window()->client()->ScheduleFrame();
}
这里传给了 RuntimeController,然后继续调用 Engine、Animator 中相关的函数,在 Animator 中的实现如下:
void Animator::RequestFrame(bool regenerate_layer_tree) {
if (regenerate_layer_tree) {
regenerate_layer_tree_ = true;
}
if (paused_ && !dimension_change_pending_) {
return;
}
if (!pending_frame_semaphore_.TryWait()) {
// Multiple calls to Animator::RequestFrame will still result in a
// single request to the VsyncWaiter.
return;
}
// The AwaitVSync is going to call us back at the next VSync. However, we want
// to be reasonably certain that the UI thread is not in the middle of a
// particularly expensive callout. We post the AwaitVSync to run right after
// an idle. This does NOT provide a guarantee that the UI thread has not
// started an expensive operation right after posting this message however.
// To support that, we need edge triggered wakes on VSync.
task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
frame_number = frame_number_]() {
if (!self.get()) {
return;
}
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
self->AwaitVSync();
});
frame_scheduled_ = true;
}
void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
if (self) {
if (self->CanReuseLastLayerTree()) {
self->DrawLastLayerTree();
} else {
self->BeginFrame(frame_start_time, frame_target_time);
}
}
});
delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}
在这里,Animator 先是切换到 UI 线程,再调用的 AwaitVSync ,AwaitVSync 中有三步工作,首先声明了一个 callback ,在 callback 里可以看到,会执行 BeginFrame(然后一步步会调用到 flutter 中的 onBegineFrame),然后调用 waiter 的 AsyncWaitForVsync,最后调用 Shell 的 OnAnimatorNotifyIdle,向其通知现在是空闲时间,可以在这段时间中执行一些其他任务,比如 CG 等。
waiter 对应的类是 VsyncWaiter,VsyncWaiter 是一个基类,在不同的平台会使用不同的子类,比如 VsyncWaiterAndroid、VsyncWaiterIOS,以 android 为例,
void VsyncWaiterAndroid::AwaitVSync() {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(weak_this);
task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {
JNIEnv* env = fml::jni::AttachCurrentThread();
env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), //
g_async_wait_for_vsync_method_, //
java_baton //
);
});
}
会先切换到 Platform 线程,再通过 jni 调用 FlutterJNI.asyncWaitForVsync 方法。
private static void asyncWaitForVsync(final long cookie) {
if (asyncWaitForVsyncDelegate != null) {
asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);
} else {
throw new IllegalStateException(
"An AsyncWaitForVsyncDelegate must be registered with FlutterJNI before asyncWaitForVsync() is invoked.");
}
}
private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
new FlutterJNI.AsyncWaitForVsyncDelegate() {
@Override
public void asyncWaitForVsync(long cookie) {
Choreographer.getInstance()
.postFrameCallback(
new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
float fps = windowManager.getDefaultDisplay().getRefreshRate();
long refreshPeriodNanos = (long) (1000000000.0 / fps);
FlutterJNI.nativeOnVsync(
frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
}
});
}
};
如上,asyncWaitForVsyncDelegate 的 asyncWaitForVsync 方法内部,是依赖于 Choreographer 实现的,向 Choreographer 中添加回调,在回调中调用 FlutterJNI.nativeOnVsync 将事件传递到 engine 中。了解 android 开发的知道,Choreographer 在 android 中就是用于生成帧的,之于 android 就相当于 SchedulerBinding 之于 flutter,只不过 SchedulerBinding 依赖于 Choreographer,Choreographer 依赖的是系统实现。
当 doFrame 方法被调用时,新的一帧开始,FlutterJNI.nativeOnVsync 在 engine 中对应的函数为 VsyncWaiterAndroid::OnNativeVsync,这个函数的三个参数的意义分别是:当前的时间、预计的下一帧开始的时间和 VsyncWaiterAndroid 的指针(cookie),后面在 jni 中还需要根据它找到对应的 VsyncWaiterAndroid 对象。
OnNativeVsync 将两个时间进行转换之后再调用了 ConsumePendingCallback,然后在这里取出 VsyncWaiterAndroid 对象,调用其 FireCallback。
FireCallback 的实现位于 VsyncWaiter 中,
void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
Callback callback;
fml::closure secondary_callback;
{
std::scoped_lock lock(callback_mutex_);
callback = std::move(callback_);
secondary_callback = std::move(secondary_callback_);
}
if (!callback && !secondary_callback) {
// This means that the vsync waiter implementation fired a callback for a
// request we did not make. This is a paranoid check but we still want to
// make sure we catch misbehaving vsync implementations.
TRACE_EVENT_INSTANT0("flutter", "MismatchedFrameCallback");
return;
}
if (callback) {
auto flow_identifier = fml::tracing::TraceNonce();
// The base trace ensures that flows have a root to begin from if one does
// not exist. The trace viewer will ignore traces that have no base event
// trace. While all our message loops insert a base trace trace
// (MessageLoop::RunExpiredTasks), embedders may not.
TRACE_EVENT0("flutter", "VsyncFireCallback");
TRACE_FLOW_BEGIN("flutter", kVsyncFlowName, flow_identifier);
task_runners_.GetUITaskRunner()->PostTaskForTime(
[callback, flow_identifier, frame_start_time, frame_target_time]() {
FML_TRACE_EVENT("flutter", kVsyncTraceName, "StartTime",
frame_start_time, "TargetTime", frame_target_time);
fml::tracing::TraceEventAsyncComplete(
"flutter", "VsyncSchedulingOverhead", fml::TimePoint::Now(),
frame_start_time);
callback(frame_start_time, frame_target_time);
TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier);
},
frame_start_time);
}
if (secondary_callback) {
task_runners_.GetUITaskRunner()->PostTaskForTime(
std::move(secondary_callback), frame_start_time);
}
}
这个函数主要的就是切换到 UI 线程执行了 callback,也就是 AsyncWaitForVsync 中传进来、在 AwaitVSync 中声明的:
void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
if (self) {
if (self->CanReuseLastLayerTree()) {
self->DrawLastLayerTree();
} else {
self->BeginFrame(frame_start_time, frame_target_time);
}
}
});
delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}
从这里开始,就需要开始进行新一帧的绘制(如果不需要重新绘制的话,会直接重用上一次的),调用 BeginFrame。
void Animator::BeginFrame(fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);
TRACE_EVENT0("flutter", "Animator::BeginFrame");
while (!trace_flow_ids_.empty()) {
uint64_t trace_flow_id = trace_flow_ids_.front();
TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
trace_flow_ids_.pop_front();
}
frame_scheduled_ = false;
notify_idle_task_id_++;
regenerate_layer_tree_ = false;
pending_frame_semaphore_.Signal();
if (!producer_continuation_) {
// We may already have a valid pipeline continuation in case a previous
// begin frame did not result in an Animation::Render. Simply reuse that
// instead of asking the pipeline for a fresh continuation.
producer_continuation_ = layer_tree_pipeline_->Produce();
if (!producer_continuation_) {
// If we still don't have valid continuation, the pipeline is currently
// full because the consumer is being too slow. Try again at the next
// frame interval.
RequestFrame();
return;
}
}
// We have acquired a valid continuation from the pipeline and are ready
// to service potential frame.
FML_DCHECK(producer_continuation_);
last_begin_frame_time_ = frame_start_time;
dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
{
TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
FrameParity());
delegate_.OnAnimatorBeginFrame(frame_target_time);
}
if (!frame_scheduled_) {
// Under certain workloads (such as our parent view resizing us, which is
// communicated to us by repeat viewport metrics events), we won't
// actually have a frame scheduled yet, despite the fact that we *will* be
// producing a frame next vsync (it will be scheduled once we receive the
// viewport event). Because of this, we hold off on calling
// |OnAnimatorNotifyIdle| for a little bit, as that could cause garbage
// collection to trigger at a highly undesirable time.
task_runners_.GetUITaskRunner()->PostDelayedTask(
[self = weak_factory_.GetWeakPtr(),
notify_idle_task_id = notify_idle_task_id_]() {
if (!self.get()) {
return;
}
// If our (this task's) task id is the same as the current one
// (meaning there were no follow up frames to the |BeginFrame| call
// that posted this task) and no frame is currently scheduled, then
// assume that we are idle, and notify the engine of this.
if (notify_idle_task_id == self->notify_idle_task_id_ &&
!self->frame_scheduled_) {
TRACE_EVENT0("flutter", "BeginFrame idle callback");
self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
100000);
}
},
kNotifyIdleTaskWaitTime);
}
}
第一步判断是否可以开始新一帧的渲染,如果 ProducerContinuation 为空,就调用 layer_tree_pipeline_->Produce 获取,如果还是没有获取到,就会直接跳过本次渲染,直接请求下一帧(这就会造成用户看起来的卡顿)。第二步就是调用 delegate_.OnAnimatorBeginFrame 将其继续往 flutter 中传递(后续的 flutter 中的渲染都在这个函数中,没有切换线程)。第三步是判断在执行完了本帧的渲染之后,是否已经进行了下一帧的请求,也就是上面 ScheduleFrame 的过程,如果没有的话,就调用 delegate_.OnAnimatorNotifyIdle,跟在 AwaitVSync 调用这个函数一样,可以用户执行一些 CG 操作,如果已经执行了 ScheduleFrame 的话,那么也应该会直接在 AwaitVSync 中调用这个函数,所以,从这个角度来看,无论是哪种方式,都会调用 delegate_.OnAnimatorNotifyIdle。
delegate_.OnAnimatorBeginFrame 后面会经过 Shell、Engine、RuntimeController、Window,最后在 Window.BegineFrame 中,依次调用了 flutter 中的 _beginFrame 和 _drawFrame 函数。从这里也可以看出,beginFrame 和 drawFrame 两个函数是先后调用的,与 flutter 中的 scheduleWarmUpFrame 调用顺序保持一致。
上面两个函数会调用 window.onBeginFrame 和 window.onDrawFrame,也就是在 SchedulerBinding.initInstances 中设置的 _handleBeginFrame 和 _handleDrawFrame,
void _handleBeginFrame(Duration rawTimeStamp) {
if (_warmUpFrame) {
assert(!_ignoreNextEngineDrawFrame);
_ignoreNextEngineDrawFrame = true;
return;
}
handleBeginFrame(rawTimeStamp);
}
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
return;
}
handleDrawFrame();
}
最后会调用到 handleBeginFrame 和 handleDrawFrame。
_handleLifecycleMessage
initInstances 中的另一个任务就是注册 _handleLifecycleMessage 处理生命周期相关的消息。
void handleAppLifecycleStateChanged(AppLifecycleState state) {
assert(state != null);
_lifecycleState = state;
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
_setFramesEnabledState(true);
break;
case AppLifecycleState.paused:
case AppLifecycleState.suspending:
_setFramesEnabledState(false);
break;
}
}
void _setFramesEnabledState(bool enabled) {
if (_framesEnabled == enabled)
return;
_framesEnabled = enabled;
if (enabled)
scheduleFrame();
}
由以上可以知道,当 flutter 依赖的 Activity(in android for example)处于活跃状态时,_framesEnabled 为真, 而 _framesEnabled 的用处,主要就是在 scheduleFrame 中:
void scheduleFrame() {
if (_hasScheduledFrame || !_framesEnabled)
return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
window.scheduleFrame();
_hasScheduledFrame = true;
}
只有当其为真时,才会执行到 window.scheduleFrame,从而停止更新视图,而当 Activity 重新变为活跃状态时,也就是 _setFramesEnabledState 参数为真时,可以看到就会调用 scheduleFrame 重新开始刷新视图。
scheduleTask
Future<T> scheduleTask<T>(
TaskCallback<T> task,
Priority priority, {
String debugLabel,
Flow flow,
}) {
final bool isFirstTask = _taskQueue.isEmpty;
final _TaskEntry<T> entry = _TaskEntry<T>(
task,
priority.value,
debugLabel,
flow,
);
_taskQueue.add(entry);
if (isFirstTask && !locked)
_ensureEventLoopCallback();
return entry.completer.future;
}
使用 scheduleTask 运行了 task 除了要提供 task 本身之外,还要提供一个 Priority,用于判断 task 是否会被执行以及何时被执行。该函数参数会被封装成 _TaskEntry,存放在 _taskQueue 中,_taskQueue 是一个 HeapPriorityQueue 对象,它内部会根据 task 的 priority 进行排序,保证每次取出的都是当前队列中优先级最高的一个。
接下来可以有三种情况:
- 该 task 是第一个待执行,且没有上锁(上锁期间不能执行 task),执行 _ensureEventLoopCallback
- 该 task 是第一个待执行,但是上锁了,就直接结束,不过当锁解除的时候,也就是在 SchedulerBinding 的 unlocked 中,如果有待执行 task,还是会执行 _ensureEventLoopCallback
- 该 task 不是第一个待执行,那么直接结束,由此可以推测,一旦开始执行 task,直到 _taskQueue 为空或者上锁了,应该会循环之行 task
再看 _ensureEventLoopCallback 的实现,
void _ensureEventLoopCallback() {
assert(!locked);
assert(_taskQueue.isNotEmpty);
if (_hasRequestedAnEventLoopCallback)
return;
_hasRequestedAnEventLoopCallback = true;
Timer.run(_runTasks);
}
void _runTasks() {
_hasRequestedAnEventLoopCallback = false;
if (handleEventLoopCallback())
_ensureEventLoopCallback(); // runs next task when there's time
}
_hasRequestedAnEventLoopCallback 用于防止多次进入,Timer.run 将会构建一个同步多线程回调 _runTasks,一般在线程空闲时执行,比如说在向 native 层请求了下一帧,但是还没有被回调之前。而在 _runTasks 中,
handleEventLoopCallback 用于处理一个 task,完了之后再调用 _ensureEventLoopCallback 循环执行一个新的 task,由此可见,_ensureEventLoopCallback 确实是一个循环执行的过程。
bool handleEventLoopCallback() {
if (_taskQueue.isEmpty || locked)
return false;
final _TaskEntry<dynamic> entry = _taskQueue.first;
if (schedulingStrategy(priority: entry.priority, scheduler: this)) {
try {
_taskQueue.removeFirst();
entry.run();
} catch (exception, exceptionStack) {
StackTrace callbackStack;
assert(() {
callbackStack = entry.debugStack;
return true;
}());
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: exceptionStack,
library: 'scheduler library',
context: ErrorDescription('during a task callback'),
informationCollector: (callbackStack == null) ? null : () sync* {
yield DiagnosticsStackTrace(
'\nThis exception was thrown in the context of a scheduler callback. '
'When the scheduler callback was _registered_ (as opposed to when the '
'exception was thrown), this was the stack',
callbackStack,
);
},
));
}
return _taskQueue.isNotEmpty;
}
return false;
}
首先还是一个状态判断,然后从 _taskQueue 取出一个 task,再进行是否可执行的判断,默认的 schedulingStrategy 实现如下:
bool defaultSchedulingStrategy({ int priority, SchedulerBinding scheduler }) {
if (scheduler.transientCallbackCount > 0)
return priority >= Priority.animation.value;
return true;
}
这也正是注释里所说的,当有动画在执行时,只有优先级不小于 Priority.animation 的才能被执行。那么如果这里判断过不去,就直接返回 false 了。如果可以执行,就调用 task.run 执行,完了返回 _taskQueue 是否为空。
当 handleEventLoopCallback 执行完了之后,_runTasks 会根据它的返回值决定是否接着调用 _ensureEventLoopCallback。返回值有两种,返回真的情况就是上一个 task 执行了,并且 _taskQueue 中还有余量,返回 false 有两种情况,一种是状态判断没过去(_taskQueue 已经空了,或者上锁了),另一种就是优先级判断没过去,取出的是当前队列中优先级最高的,如果这个最高的都判断过不去,可以认为队列中剩余的 task 都是不能被执行的,只能等动画执行完了,但是目前好像并没有发现有在动画执行完的时候再启动这个循环(启动循环的方式只有两种,解锁和添加新的 task)。
总结
从以上来看,SchedulerBinding 的功能有两个,一是用于确定视图渲染的时间,在 flutter 应用运行的过程中,可以通过 setState 等方式请求刷新视图,此时 SchedulerBinding 接收到请求之后会再向 native 层添加回调,由 native 确定何时去执行视图刷新,同时 SchedulerBinding 还支持 flutter 中其他组件添加一些与帧渲染相关的回调,比如动画需要在渲染 widgets 之前更新 widgets 的内容等。另一方面,SchedulerBinding 还支持在帧渲染的间隙执行一些其他任务,这些任务需要只能在非渲染阶段执行,且受到优先级的限制,为的是能够使 UI 更流畅,如果要保证任务及时、更快地执行,可以使用 Future(在非渲染阶段执行,但是没有优先级限制)、Isolate(开辟新线程,不受 UI 线程影响) 等方式。