arduino
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// 中文:版权所有 2014 The Flutter Authors。保留所有权利。
// 中文:本代码的使用受 BSD 许可证的约束,该许可证可在
// 中文:LICENSE 文件中找到。
/// @docImport 'package:flutter/material.dart';
/// @docImport 'package:flutter/rendering.dart';
/// @docImport 'package:flutter/services.dart';
/// @docImport 'package:flutter/widgets.dart';
///
/// @docImport 'ticker.dart';
library;
import 'dart:async';
import 'dart:collection';
import 'dart:developer' show Flow, Timeline, TimelineTask;
import 'dart:ui' show AppLifecycleState, DartPerformanceMode, FramePhase, FrameTiming, PlatformDispatcher, TimingsCallback;
import 'package:collection/collection.dart' show HeapPriorityQueue, PriorityQueue;
import 'package:flutter/foundation.dart';
import 'debug.dart';
import 'priority.dart';
import 'service_extensions.dart';
export 'dart:ui' show AppLifecycleState, FrameTiming, TimingsCallback;
export 'priority.dart' show Priority;
/// Slows down animations by this factor to help in development.
/// 中文:通过此因子减慢动画速度,以帮助开发。
double get timeDilation => _timeDilation;
double _timeDilation = 1.0;
/// If the [SchedulerBinding] has been initialized, setting the time dilation
/// automatically calls [SchedulerBinding.resetEpoch] to ensure that time stamps
/// seen by consumers of the scheduler binding are always increasing.
///
/// It is safe to set this before initializing the binding.
/// 中文:如果 [SchedulerBinding] 已经初始化,设置时间膨胀
/// 中文:会自动调用 [SchedulerBinding.resetEpoch] 以确保调度器绑定的消费者
/// 中文:看到的时间戳总是递增的。
///
/// 中文:在初始化绑定之前设置这个值是安全的。
set timeDilation(double value) {
assert(value > 0.0);
if (_timeDilation == value) {
return;
}
// If the binding has been created, we need to resetEpoch first so that we
// capture start of the epoch with the current time dilation.
// 中文:如果绑定已经创建,我们需要先调用resetEpoch,以便
// 中文:使用当前的时间膨胀捕获时代的开始。
SchedulerBinding._instance?.resetEpoch();
_timeDilation = value;
}
/// Signature for frame-related callbacks from the scheduler.
///
/// The `timeStamp` is the number of milliseconds since the beginning of the
/// scheduler's epoch. Use timeStamp to determine how far to advance animation
/// timelines so that all the animations in the system are synchronized to a
/// common time base.
/// 中文:调度器的帧相关回调的签名。
///
/// 中文:`timeStamp` 是自调度器纪元开始以来的毫秒数。使用timeStamp来确定
/// 中文:动画时间线应该前进多少,以便系统中的所有动画都同步到
/// 中文:一个共同的时间基准。
typedef FrameCallback = void Function(Duration timeStamp);
java
/// Signature for [SchedulerBinding.scheduleTask] callbacks.
///
/// The type argument `T` is the task's return value. Consider `void` if the
/// task does not return a value.
/// 中文:[SchedulerBinding.scheduleTask] 回调的签名。
///
/// 中文:类型参数 `T` 是任务的返回值。如果任务不返回值,可以考虑使用 `void`。
typedef TaskCallback<T> = FutureOr<T> Function();
/// Signature for the [SchedulerBinding.schedulingStrategy] callback. Called
/// whenever the system needs to decide whether a task at a given
/// priority needs to be run.
///
/// Return true if a task with the given priority should be executed at this
/// time, false otherwise.
///
/// See also:
///
/// * [defaultSchedulingStrategy], the default [SchedulingStrategy] for [SchedulerBinding.schedulingStrategy].
/// 中文:[SchedulerBinding.schedulingStrategy] 回调的签名。每当系统需要
/// 中文:决定是否应该执行给定优先级的任务时调用。
///
/// 中文:如果应该在此时执行具有给定优先级的任务,则返回true,否则返回false。
///
/// 中文:另请参阅:
///
/// 中文: * [defaultSchedulingStrategy],[SchedulerBinding.schedulingStrategy]的默认[SchedulingStrategy]。
typedef SchedulingStrategy = bool Function({ required int priority, required SchedulerBinding scheduler });
class _TaskEntry<T> {
_TaskEntry(this.task, this.priority, this.debugLabel, this.flow) {
assert(() {
debugStack = StackTrace.current;
return true;
}());
}
final TaskCallback<T> task;
final int priority;
final String? debugLabel;
final Flow? flow;
late StackTrace debugStack;
final Completer<T> completer = Completer<T>();
void run() {
if (!kReleaseMode) {
Timeline.timeSync(
debugLabel ?? 'Scheduled Task',
() {
completer.complete(task());
},
flow: flow != null ? Flow.step(flow!.id) : null,
);
} else {
completer.complete(task());
}
}
}
class _FrameCallbackEntry {
_FrameCallbackEntry(this.callback, { bool rescheduling = false }) {
assert(() {
if (rescheduling) {
assert(() {
if (debugCurrentCallbackStack == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('scheduleFrameCallback called with rescheduling true, but no callback is in scope.'),
// 中文:scheduleFrameCallback被调用时rescheduling为true,但没有回调在作用域内。
ErrorDescription(
'The "rescheduling" argument should only be set to true if the '
'callback is being reregistered from within the callback itself, '
'and only then if the callback itself is entirely synchronous.',
// 中文:"rescheduling"参数应该只在回调本身被重新注册时设置为true,
// 中文:并且只有在回调本身完全是同步的情况下。
),
ErrorHint(
'If this is the initial registration of the callback, or if the '
'callback is asynchronous, then do not use the "rescheduling" '
'argument.',
// 中文:如果这是回调的初始注册,或者回调是异步的,
// 中文:那么不要使用"rescheduling"参数。
),
]);
}
return true;
}());
debugStack = debugCurrentCallbackStack;
} else {
// TODO(ianh): trim the frames from this library, so that the call to scheduleFrameCallback is the top one
// 中文:TODO(ianh): 从这个库中修剪帧,使对scheduleFrameCallback的调用成为顶部的帧
debugStack = StackTrace.current;
}
return true;
}());
}
final FrameCallback callback;
static StackTrace? debugCurrentCallbackStack;
StackTrace? debugStack;
}
/// The various phases that a [SchedulerBinding] goes through during
/// [SchedulerBinding.handleBeginFrame].
///
/// This is exposed by [SchedulerBinding.schedulerPhase].
///
/// The values of this enum are ordered in the same order as the phases occur,
/// so their relative index values can be compared to each other.
///
/// See also:
///
/// * [WidgetsBinding.drawFrame], which pumps the build and rendering pipeline
/// to generate a frame.
/// 中文:[SchedulerBinding]在[SchedulerBinding.handleBeginFrame]期间经历的各个阶段。
///
/// 中文:这由[SchedulerBinding.schedulerPhase]暴露。
///
/// 中文:此枚举的值按照阶段发生的顺序排序,
/// 中文:因此它们的相对索引值可以相互比较。
///
/// 中文:另请参阅:
///
/// 中文: * [WidgetsBinding.drawFrame],它泵送构建和渲染管道
/// 中文: 以生成一帧。
enum SchedulerPhase {
/// No frame is being processed. Tasks (scheduled by
/// [SchedulerBinding.scheduleTask]), microtasks (scheduled by
/// [scheduleMicrotask]), [Timer] callbacks, event handlers (e.g. from user
/// input), and other callbacks (e.g. from [Future]s, [Stream]s, and the like)
/// may be executing.
/// 中文:没有帧正在处理。任务(由[SchedulerBinding.scheduleTask]调度),
/// 中文:微任务(由[scheduleMicrotask]调度),[Timer]回调,事件处理程序
/// 中文:(例如来自用户输入),以及其他回调(例如来自[Future],[Stream]等)
/// 中文:可能正在执行。
idle,
/// The transient callbacks (scheduled by
/// [SchedulerBinding.scheduleFrameCallback]) are currently executing.
///
/// Typically, these callbacks handle updating objects to new animation
/// states.
///
/// See [SchedulerBinding.handleBeginFrame].
/// 中文:瞬态回调(由[SchedulerBinding.scheduleFrameCallback]调度)
/// 中文:当前正在执行。
///
/// 中文:通常,这些回调处理将对象更新到新的动画状态。
///
/// 中文:参见[SchedulerBinding.handleBeginFrame]。
transientCallbacks,
/// Microtasks scheduled during the processing of transient callbacks are
/// current executing.
///
/// This may include, for instance, callbacks from futures resolved during the
/// [transientCallbacks] phase.
/// 中文:在处理瞬态回调期间调度的微任务当前正在执行。
///
/// 中文:这可能包括,例如,在[transientCallbacks]阶段解析的future的回调。
midFrameMicrotasks,
/// The persistent callbacks (scheduled by
/// [SchedulerBinding.addPersistentFrameCallback]) are currently executing.
///
/// Typically, this is the build/layout/paint pipeline. See
/// [WidgetsBinding.drawFrame] and [SchedulerBinding.handleDrawFrame].
/// 中文:持久回调(由[SchedulerBinding.addPersistentFrameCallback]调度)
/// 中文:当前正在执行。
///
/// 中文:通常,这是构建/布局/绘制管道。参见
/// 中文:[WidgetsBinding.drawFrame]和[SchedulerBinding.handleDrawFrame]。
persistentCallbacks,
/// The post-frame callbacks (scheduled by
/// [SchedulerBinding.addPostFrameCallback]) are currently executing.
///
/// Typically, these callbacks handle cleanup and scheduling of work for the
/// next frame.
///
/// See [SchedulerBinding.handleDrawFrame].
/// 中文:帧后回调(由[SchedulerBinding.addPostFrameCallback]调度)
/// 中文:当前正在执行。
///
/// 中文:通常,这些回调处理清理和为下一帧调度工作。
///
/// 中文:参见[SchedulerBinding.handleDrawFrame]。
postFrameCallbacks,
}
kotlin
/// This callback is invoked when a request for [DartPerformanceMode] is disposed.
///
/// See also:
///
/// * [PerformanceModeRequestHandle] for more information on the lifecycle of the handle.
/// 中文:当[DartPerformanceMode]请求被处置时调用此回调。
///
/// 中文:另请参阅:
///
/// 中文:* [PerformanceModeRequestHandle] 有关句柄生命周期的更多信息。
typedef _PerformanceModeCleanupCallback = VoidCallback;
/// An opaque handle that keeps a request for [DartPerformanceMode] active until
/// disposed.
///
/// To create a [PerformanceModeRequestHandle], use [SchedulerBinding.requestPerformanceMode].
/// The component that makes the request is responsible for disposing the handle.
/// 中文:一个不透明的句柄,它保持对[DartPerformanceMode]的请求处于活动状态,直到
/// 中文:被处置。
///
/// 中文:要创建[PerformanceModeRequestHandle],请使用[SchedulerBinding.requestPerformanceMode]。
/// 中文:发出请求的组件负责处置句柄。
class PerformanceModeRequestHandle {
PerformanceModeRequestHandle._(_PerformanceModeCleanupCallback this._cleanup) {
// TODO(polina-c): stop duplicating code across disposables
// [https://github.com/flutter/flutter/issues/137435](https://github.com/flutter/flutter/issues/137435)
// 中文:TODO(polina-c): 停止在可处置对象间复制代码
// 中文:[https://github.com/flutter/flutter/issues/137435](https://github.com/flutter/flutter/issues/137435)
if (kFlutterMemoryAllocationsEnabled) {
FlutterMemoryAllocations.instance.dispatchObjectCreated(
library: 'package:flutter/scheduler.dart',
className: '$PerformanceModeRequestHandle',
object: this,
);
}
}
_PerformanceModeCleanupCallback? _cleanup;
/// Call this method to signal to [SchedulerBinding] that a request for a [DartPerformanceMode]
/// is no longer needed.
///
/// This method must only be called once per object.
/// 中文:调用此方法向[SchedulerBinding]表明对[DartPerformanceMode]的请求
/// 中文:不再需要。
///
/// 中文:此方法每个对象只能调用一次。
void dispose() {
assert(_cleanup != null);
// TODO(polina-c): stop duplicating code across disposables
// [https://github.com/flutter/flutter/issues/137435](https://github.com/flutter/flutter/issues/137435)
// 中文:TODO(polina-c): 停止在可处置对象间复制代码
// 中文:[https://github.com/flutter/flutter/issues/137435](https://github.com/flutter/flutter/issues/137435)
if (kFlutterMemoryAllocationsEnabled) {
FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
_cleanup!();
_cleanup = null;
}
}
scss
/// Scheduler for running the following:
///
/// * _Transient callbacks_, triggered by the system's
/// [dart:ui.PlatformDispatcher.onBeginFrame] callback, for synchronizing the
/// application's behavior to the system's display. For example, [Ticker]s and
/// [AnimationController]s trigger from these.
///
/// * _Persistent callbacks_, triggered by the system's
/// [dart:ui.PlatformDispatcher.onDrawFrame] callback, for updating the
/// system's display after transient callbacks have executed. For example, the
/// rendering layer uses this to drive its rendering pipeline.
///
/// * _Post-frame callbacks_, which are run after persistent callbacks, just
/// before returning from the [dart:ui.PlatformDispatcher.onDrawFrame] callback.
///
/// * Non-rendering tasks, to be run between frames. These are given a
/// priority and are executed in priority order according to a
/// [schedulingStrategy].
/// 中文:用于运行以下内容的调度器:
///
/// 中文:* _瞬态回调_,由系统的[dart:ui.PlatformDispatcher.onBeginFrame]回调触发,
/// 中文: 用于将应用程序的行为与系统的显示同步。例如,[Ticker]和
/// 中文: [AnimationController]从这些触发。
///
/// 中文:* _持久回调_,由系统的[dart:ui.PlatformDispatcher.onDrawFrame]回调触发,
/// 中文: 用于在瞬态回调执行后更新系统的显示。例如,渲染层使用
/// 中文: 这个来驱动其渲染管道。
///
/// 中文:* _帧后回调_,在持久回调之后运行,就在从
/// 中文: [dart:ui.PlatformDispatcher.onDrawFrame]回调返回之前。
///
/// 中文:* 非渲染任务,在帧之间运行。这些被赋予优先级,并按照
/// 中文: [schedulingStrategy]确定的优先级顺序执行。
mixin SchedulerBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
_instance = this;
if (!kReleaseMode) {
addTimingsCallback((List<FrameTiming> timings) {
timings.forEach(_profileFramePostEvent);
});
}
}
/// The current [SchedulerBinding], if one has been created.
///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
/// 中文:当前的[SchedulerBinding],如果已创建。
///
/// 中文:提供对此mixin暴露的功能的访问。绑定必须在使用此getter之前
/// 中文:初始化;这通常通过调用[runApp]或[WidgetsFlutterBinding.ensureInitialized]完成。
static SchedulerBinding get instance => BindingBase.checkInstance(_instance);
static SchedulerBinding? _instance;
final List<TimingsCallback> _timingsCallbacks = <TimingsCallback>[];
java
/// Add a [TimingsCallback] that receives [FrameTiming] sent from
/// the engine.
///
/// This API enables applications to monitor their graphics
/// performance. Data from the engine is batched into lists of
/// [FrameTiming] objects which are reported approximately once a
/// second in release mode and approximately once every 100ms in
/// debug and profile builds. The list is sorted in ascending
/// chronological order (earliest frame first). The timing of the
/// first frame is sent immediately without batching.
///
/// The data returned can be used to catch missed frames (by seeing
/// if [FrameTiming.buildDuration] or [FrameTiming.rasterDuration]
/// exceed the frame budget, e.g. 16ms at 60Hz), and to catch high
/// latency (by seeing if [FrameTiming.totalSpan] exceeds the frame
/// budget). It is possible for no frames to be missed but for the
/// latency to be more than one frame in the case where the Flutter
/// engine is pipelining the graphics updates, e.g. because the sum
/// of the [FrameTiming.buildDuration] and the
/// [FrameTiming.rasterDuration] together exceed the frame budget.
/// In those cases, animations will be smooth but touch input will
/// feel more sluggish.
///
/// Using [addTimingsCallback] is preferred over using
/// [dart:ui.PlatformDispatcher.onReportTimings] directly because the
/// [dart:ui.PlatformDispatcher.onReportTimings] API only allows one callback,
/// which prevents multiple libraries from registering listeners
/// simultaneously, while this API allows multiple callbacks to be registered
/// independently.
///
/// This API is implemented in terms of
/// [dart:ui.PlatformDispatcher.onReportTimings]. In release builds, when no
/// libraries have registered with this API, the
/// [dart:ui.PlatformDispatcher.onReportTimings] callback is not set, which
/// disables the performance tracking and reduces the runtime overhead to
/// approximately zero. The performance overhead of the performance tracking
/// when one or more callbacks are registered (i.e. when it is enabled) is
/// very approximately 0.01% CPU usage per second (measured on an iPhone 6s).
///
/// In debug and profile builds, the [SchedulerBinding] itself
/// registers a timings callback to update the [Timeline].
///
/// If the same callback is added twice, it will be executed twice.
///
/// See also:
///
/// * [removeTimingsCallback], which can be used to remove a callback
/// added using this method.
/// 中文:添加一个[TimingsCallback],接收从引擎发送的[FrameTiming]。
///
/// 中文:此API使应用程序能够监控其图形性能。来自引擎的数据被批处理成
/// 中文:[FrameTiming]对象列表,在发布模式下大约每秒报告一次,
/// 中文:在调试和配置文件构建中大约每100毫秒报告一次。列表按升序
/// 中文:时间顺序排序(最早的帧在前)。第一帧的计时立即发送,
/// 中文:不进行批处理。
///
/// 中文:返回的数据可用于捕获丢失的帧(通过查看
/// 中文:[FrameTiming.buildDuration]或[FrameTiming.rasterDuration]
/// 中文:是否超过帧预算,例如60Hz时为16毫秒),以及捕获高
/// 中文:延迟(通过查看[FrameTiming.totalSpan]是否超过帧
/// 中文:预算)。在没有丢失帧但延迟超过一帧的情况下,
/// 中文:Flutter引擎正在流水线处理图形更新,例如因为
/// 中文:[FrameTiming.buildDuration]和[FrameTiming.rasterDuration]
/// 中文:的总和超过了帧预算。在这些情况下,动画将是平滑的,
/// 中文:但触摸输入会感觉更加迟缓。
///
/// 中文:使用[addTimingsCallback]比直接使用
/// 中文:[dart:ui.PlatformDispatcher.onReportTimings]更好,因为
/// 中文:[dart:ui.PlatformDispatcher.onReportTimings] API只允许一个回调,
/// 中文:这阻止了多个库同时注册监听器,而此API允许
/// 中文:独立注册多个回调。
///
/// 中文:此API是基于[dart:ui.PlatformDispatcher.onReportTimings]实现的。
/// 中文:在发布构建中,当没有库通过此API注册时,
/// 中文:[dart:ui.PlatformDispatcher.onReportTimings]回调不会设置,
/// 中文:这会禁用性能跟踪并将运行时开销降低到
/// 中文:接近零。当注册了一个或多个回调(即启用时)的性能跟踪的
/// 中文:性能开销非常小,大约为每秒0.01%的CPU使用率(在iPhone 6s上测量)。
///
/// 中文:在调试和配置文件构建中,[SchedulerBinding]本身
/// 中文:注册一个计时回调来更新[Timeline]。
///
/// 中文:如果同一回调被添加两次,它将被执行两次。
///
/// 中文:另请参阅:
///
/// 中文: * [removeTimingsCallback],可用于删除使用此方法
/// 中文: 添加的回调。
void addTimingsCallback(TimingsCallback callback) {
_timingsCallbacks.add(callback);
if (_timingsCallbacks.length == 1) {
assert(platformDispatcher.onReportTimings == null);
platformDispatcher.onReportTimings = _executeTimingsCallbacks;
}
assert(platformDispatcher.onReportTimings == _executeTimingsCallbacks);
}
/// Removes a callback that was earlier added by [addTimingsCallback].
/// 中文:删除先前由[addTimingsCallback]添加的回调。
void removeTimingsCallback(TimingsCallback callback) {
assert(_timingsCallbacks.contains(callback));
_timingsCallbacks.remove(callback);
if (_timingsCallbacks.isEmpty) {
platformDispatcher.onReportTimings = null;
}
}
@pragma('vm:notify-debugger-on-exception')
void _executeTimingsCallbacks(List<FrameTiming> timings) {
final List<TimingsCallback> clonedCallbacks =
List<TimingsCallback>.of(_timingsCallbacks);
for (final TimingsCallback callback in clonedCallbacks) {
try {
if (_timingsCallbacks.contains(callback)) {
callback(timings);
}
} catch (exception, stack) {
InformationCollector? collector;
assert(() {
collector = () => <DiagnosticsNode>[
DiagnosticsProperty<TimingsCallback>(
'The TimingsCallback that gets executed was',
// 中文:执行的TimingsCallback是
callback,
style: DiagnosticsTreeStyle.errorProperty,
),
];
return true;
}());
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
context: ErrorDescription('while executing callbacks for FrameTiming'),
// 中文:执行FrameTiming回调时
informationCollector: collector,
));
}
}
}
@override
void initServiceExtensions() {
super.initServiceExtensions();
if (!kReleaseMode) {
registerNumericServiceExtension(
name: SchedulerServiceExtensions.timeDilation.name,
getter: () async => timeDilation,
setter: (double value) async {
timeDilation = value;
},
);
}
}
/// Whether the application is visible, and if so, whether it is currently
/// interactive.
///
/// This is set by [handleAppLifecycleStateChanged] when the
/// [SystemChannels.lifecycle] notification is dispatched.
///
/// The preferred ways to watch for changes to this value are using
/// [WidgetsBindingObserver.didChangeAppLifecycleState], or through an
/// [AppLifecycleListener] object.
/// 中文:应用程序是否可见,如果可见,当前是否可交互。
///
/// 中文:这由[handleAppLifecycleStateChanged]在[SystemChannels.lifecycle]
/// 中文:通知被分派时设置。
///
/// 中文:观察此值变化的首选方法是使用
/// 中文:[WidgetsBindingObserver.didChangeAppLifecycleState],或通过
/// 中文:[AppLifecycleListener]对象。
AppLifecycleState? get lifecycleState => _lifecycleState;
AppLifecycleState? _lifecycleState;
/// Allows the test framework to reset the lifecycle state and framesEnabled
/// back to their initial values.
/// 中文:允许测试框架将生命周期状态和framesEnabled
/// 中文:重置回初始值。
@visibleForTesting
void resetInternalState() {
_lifecycleState = null;
_framesEnabled = true;
}
/// Called when the application lifecycle state changes.
///
/// Notifies all the observers using
/// [WidgetsBindingObserver.didChangeAppLifecycleState].
///
/// This method exposes notifications from [SystemChannels.lifecycle].
/// 中文:当应用程序生命周期状态改变时调用。
///
/// 中文:使用[WidgetsBindingObserver.didChangeAppLifecycleState]
/// 中文:通知所有观察者。
///
/// 中文:此方法暴露来自[SystemChannels.lifecycle]的通知。
@protected
@mustCallSuper
void handleAppLifecycleStateChanged(AppLifecycleState state) {
if (lifecycleState == state) {
return;
}
_lifecycleState = state;
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
_setFramesEnabledState(true);
case AppLifecycleState.hidden:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
_setFramesEnabledState(false);
}
}
/// The strategy to use when deciding whether to run a task or not.
///
/// Defaults to [defaultSchedulingStrategy].
/// 中文:决定是否运行任务的策略。
///
/// 中文:默认为[defaultSchedulingStrategy]。
SchedulingStrategy schedulingStrategy = defaultSchedulingStrategy;
static int _taskSorter (_TaskEntry<dynamic> e1, _TaskEntry<dynamic> e2) {
return -e1.priority.compareTo(e2.priority);
}
final PriorityQueue<_TaskEntry<dynamic>> _taskQueue = HeapPriorityQueue<_TaskEntry<dynamic>>(_taskSorter);
java
/// Schedules the given `task` with the given `priority`.
///
/// If `task` returns a future, the future returned by [scheduleTask] will
/// complete after the former future has been scheduled to completion.
/// Otherwise, the returned future for [scheduleTask] will complete with the
/// same value returned by `task` after it has been scheduled.
///
/// The `debugLabel` and `flow` are used to report the task to the [Timeline],
/// for use when profiling.
///
/// ## Processing model
///
/// Tasks will be executed between frames, in priority order,
/// excluding tasks that are skipped by the current
/// [schedulingStrategy]. Tasks should be short (as in, up to a
/// millisecond), so as to not cause the regular frame callbacks to
/// get delayed.
///
/// If an animation is running, including, for instance, a [ProgressIndicator]
/// indicating that there are pending tasks, then tasks with a priority below
/// [Priority.animation] won't run (at least, not with the
/// [defaultSchedulingStrategy]; this can be configured using
/// [schedulingStrategy]).
/// 中文:使用给定的`priority`调度给定的`task`。
///
/// 中文:如果`task`返回future,则[scheduleTask]返回的future将
/// 中文:在前者future被调度完成后完成。
/// 中文:否则,[scheduleTask]返回的future将在`task`
/// 中文:被调度后以与`task`返回的相同值完成。
///
/// 中文:`debugLabel`和`flow`用于在分析时向[Timeline]报告任务。
///
/// 中文:## 处理模型
///
/// 中文:任务将在帧之间按优先级顺序执行,
/// 中文:排除当前[schedulingStrategy]跳过的任务。任务应该很短
/// 中文:(最多一毫秒),以免导致常规帧回调延迟。
///
/// 中文:如果动画正在运行,包括,例如,[ProgressIndicator]
/// 中文:指示有待处理的任务,那么优先级低于
/// 中文:[Priority.animation]的任务不会运行(至少,不会使用
/// 中文:[defaultSchedulingStrategy];这可以通过
/// 中文:[schedulingStrategy]配置)。
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;
}
@override
void unlocked() {
super.unlocked();
if (_taskQueue.isNotEmpty) {
_ensureEventLoopCallback();
}
}
// Whether this scheduler already requested to be called from the event loop.
// 中文:此调度器是否已经请求从事件循环中调用。
bool _hasRequestedAnEventLoopCallback = false;
// Ensures that the scheduler services a task scheduled by
// [SchedulerBinding.scheduleTask].
// 中文:确保调度器服务由[SchedulerBinding.scheduleTask]调度的任务。
void _ensureEventLoopCallback() {
assert(!locked);
assert(_taskQueue.isNotEmpty);
if (_hasRequestedAnEventLoopCallback) {
return;
}
_hasRequestedAnEventLoopCallback = true;
Timer.run(_runTasks);
}
// Scheduled by _ensureEventLoopCallback.
// 中文:由_ensureEventLoopCallback调度。
void _runTasks() {
_hasRequestedAnEventLoopCallback = false;
if (handleEventLoopCallback()) {
_ensureEventLoopCallback();
} // runs next task when there's time
// 中文:当有时间时运行下一个任务
}
/// Execute the highest-priority task, if it is of a high enough priority.
///
/// Returns false if the scheduler is [locked], or if there are no tasks
/// remaining.
///
/// Returns true otherwise, including when no task is executed due to priority
/// being too low.
/// 中文:执行最高优先级的任务,如果它的优先级足够高。
///
/// 中文:如果调度器[locked],或者没有剩余任务,则返回false。
///
/// 中文:否则返回true,包括当由于优先级太低而没有执行任务的情况。
@visibleForTesting
@pragma('vm:notify-debugger-on-exception')
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 : () {
return <DiagnosticsNode>[
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',
// 中文:\n这个异常是在调度器回调的上下文中抛出的。
// 中文:当调度器回调被_注册_(而不是异常被抛出)时,这是堆栈
callbackStack,
),
];
},
));
}
return _taskQueue.isNotEmpty;
}
return true;
}
csharp
int _nextFrameCallbackId = 0; // positive
Map<int, _FrameCallbackEntry> _transientCallbacks = <int, _FrameCallbackEntry>{};
final Set<int> _removedIds = HashSet<int>();
/// The current number of transient frame callbacks scheduled.
///
/// This is reset to zero just before all the currently scheduled
/// transient callbacks are called, at the start of a frame.
///
/// This number is primarily exposed so that tests can verify that
/// there are no unexpected transient callbacks still registered
/// after a test's resources have been gracefully disposed.
/// 中文:当前调度的瞬态帧回调数量。
///
/// 中文:这在帧开始时,在调用所有当前调度的瞬态回调之前,
/// 中文:重置为零。
///
/// 中文:这个数字主要是暴露出来,以便测试可以验证
/// 中文:在测试的资源被优雅地处置后没有意外的瞬态回调
/// 中文:仍然注册。
int get transientCallbackCount => _transientCallbacks.length;
/// Schedules the given transient frame callback.
///
/// Adds the given callback to the list of frame callbacks and ensures that a
/// frame is scheduled.
///
/// If this is called during the frame's animation phase (when transient frame
/// callbacks are still being invoked), a new frame will be scheduled, and
/// `callback` will be called in the newly scheduled frame, not in the current
/// frame.
///
/// If this is a one-off registration, ignore the `rescheduling` argument.
///
/// If this is a callback that will be re-registered each time it fires, then
/// when you re-register the callback, set the `rescheduling` argument to
/// true. This has no effect in release builds, but in debug builds, it
/// ensures that the stack trace that is stored for this callback is the
/// original stack trace for when the callback was _first_ registered, rather
/// than the stack trace for when the callback is re-registered. This makes it
/// easier to track down the original reason that a particular callback was
/// called. If `rescheduling` is true, the call must be in the context of a
/// frame callback.
///
/// Callbacks registered with this method can be canceled using
/// [cancelFrameCallbackWithId].
///
/// See also:
///
/// * [WidgetsBinding.drawFrame], which explains the phases of each frame
/// for those apps that use Flutter widgets (and where transient frame
/// callbacks fit into those phases).
/// 中文:调度给定的瞬态帧回调。
///
/// 中文:将给定的回调添加到帧回调列表中,并确保帧被调度。
///
/// 中文:如果这是在帧的动画阶段调用的(当瞬态帧回调仍在被调用时),
/// 中文:将会调度一个新帧,并且`callback`将在新调度的帧中被调用,
/// 中文:而不是在当前帧中。
///
/// 中文:如果这是一次性注册,忽略`rescheduling`参数。
///
/// 中文:如果这是一个每次触发时都会重新注册的回调,那么
/// 中文:当你重新注册回调时,将`rescheduling`参数设置为
/// 中文:true。这在发布构建中没有效果,但在调试构建中,它
/// 中文:确保为此回调存储的堆栈跟踪是回调_首次_注册时的
/// 中文:原始堆栈跟踪,而不是回调被重新注册时的堆栈跟踪。
/// 中文:这使得更容易追踪特定回调被调用的原始原因。
/// 中文:如果`rescheduling`为true,调用必须在帧回调的上下文中。
///
/// 中文:使用此方法注册的回调可以使用[cancelFrameCallbackWithId]取消。
///
/// 中文:另请参阅:
///
/// 中文: * [WidgetsBinding.drawFrame],它解释了使用Flutter小部件的应用程序
/// 中文: 每帧的各个阶段(以及瞬态帧回调适合这些阶段的位置)。
int scheduleFrameCallback(FrameCallback callback, { bool rescheduling = false }) {
scheduleFrame();
_nextFrameCallbackId += 1;
_transientCallbacks[_nextFrameCallbackId] = _FrameCallbackEntry(callback, rescheduling: rescheduling);
return _nextFrameCallbackId;
}
/// Cancels the transient frame callback with the given [id].
///
/// Removes the given callback from the list of frame callbacks. If a frame
/// has been requested, this does not also cancel that request.
///
/// Transient frame callbacks are those registered using
/// [scheduleFrameCallback].
/// 中文:取消具有给定[id]的瞬态帧回调。
///
/// 中文:从帧回调列表中删除给定的回调。如果已经请求了一帧,
/// 中文:这不会同时取消该请求。
///
/// 中文:瞬态帧回调是使用[scheduleFrameCallback]注册的回调。
void cancelFrameCallbackWithId(int id) {
assert(id > 0);
_transientCallbacks.remove(id);
_removedIds.add(id);
}
arduino
/// Asserts that there are no registered transient callbacks; if
/// there are, prints their locations and throws an exception.
///
/// A transient frame callback is one that was registered with
/// [scheduleFrameCallback].
///
/// This is expected to be called at the end of tests (the
/// flutter_test framework does it automatically in normal cases).
///
/// Call this method when you expect there to be no transient
/// callbacks registered, in an assert statement with a message that
/// you want printed when a transient callback is registered:
///
/// ```dart
/// assert(SchedulerBinding.instance.debugAssertNoTransientCallbacks(
/// 'A leak of transient callbacks was detected while doing foo.'
/// ));
/// ```
///
/// Does nothing if asserts are disabled. Always returns true.
/// 中文:断言没有注册的瞬态回调;如果
/// 中文:有,打印它们的位置并抛出异常。
///
/// 中文:瞬态帧回调是使用[scheduleFrameCallback]注册的回调。
///
/// 中文:这预期在测试结束时调用(flutter_test框架在正常情况下会自动执行)。
///
/// 中文:当你期望没有瞬态回调注册时,在断言语句中调用此方法,
/// 中文:并带有你希望在瞬态回调被注册时打印的消息:
///
/// ```dart
/// assert(SchedulerBinding.instance.debugAssertNoTransientCallbacks(
/// '在执行foo时检测到瞬态回调泄漏。'
/// ));
/// ```
///
/// 中文:如果断言被禁用,则不执行任何操作。始终返回true。
bool debugAssertNoTransientCallbacks(String reason) {
assert(() {
if (transientCallbackCount > 0) {
// We cache the values so that we can produce them later
// even if the information collector is called after
// the problem has been resolved.
// 中文:我们缓存这些值,以便稍后可以生成它们
// 中文:即使信息收集器在问题解决后被调用。
final int count = transientCallbackCount;
final Map<int, _FrameCallbackEntry> callbacks = Map<int, _FrameCallbackEntry>.of(_transientCallbacks);
FlutterError.reportError(FlutterErrorDetails(
exception: reason,
library: 'scheduler library',
informationCollector: () => <DiagnosticsNode>[
if (count == 1)
// TODO(jacobr): I have added an extra line break in this case.
// 中文:TODO(jacobr): 我在这种情况下添加了一个额外的换行符。
ErrorDescription(
'There was one transient callback left. '
'The stack trace for when it was registered is as follows:',
// 中文:有一个瞬态回调剩余。
// 中文:它注册时的堆栈跟踪如下:
)
else
ErrorDescription(
'There were $count transient callbacks left. '
'The stack traces for when they were registered are as follows:',
// 中文:有$count个瞬态回调剩余。
// 中文:它们注册时的堆栈跟踪如下:
),
for (final int id in callbacks.keys)
DiagnosticsStackTrace('── callback $id ──', callbacks[id]!.debugStack, showSeparator: false),
],
));
}
return true;
}());
return true;
}
/// Asserts that there are no pending performance mode requests in debug mode.
///
/// Throws a [FlutterError] if there are pending performance mode requests,
/// as this indicates a potential memory leak.
/// 中文:在调试模式下断言没有待处理的性能模式请求。
///
/// 中文:如果有待处理的性能模式请求,则抛出[FlutterError],
/// 中文:因为这表明潜在的内存泄漏。
bool debugAssertNoPendingPerformanceModeRequests(String reason) {
assert(() {
if (_performanceMode != null) {
throw FlutterError(reason);
}
return true;
}());
return true;
}
/// Asserts that there is no artificial time dilation in debug mode.
///
/// Throws a [FlutterError] if there are such dilation, as this will make
/// subsequent tests see dilation and thus flaky.
/// 中文:在调试模式下断言没有人为的时间膨胀。
///
/// 中文:如果有这样的膨胀,则抛出[FlutterError],因为这会使
/// 中文:后续测试看到膨胀,从而变得不稳定。
bool debugAssertNoTimeDilation(String reason) {
assert(() {
if (timeDilation != 1.0) {
throw FlutterError(reason);
}
return true;
}());
return true;
}
scss
/// Prints the stack for where the current transient callback was registered.
///
/// A transient frame callback is one that was registered with
/// [scheduleFrameCallback].
///
/// When called in debug more and in the context of a transient callback, this
/// function prints the stack trace from where the current transient callback
/// was registered (i.e. where it first called [scheduleFrameCallback]).
///
/// When called in debug mode in other contexts, it prints a message saying
/// that this function was not called in the context a transient callback.
///
/// In release mode, this function does nothing.
///
/// To call this function, use the following code:
///
/// ```dart
/// SchedulerBinding.debugPrintTransientCallbackRegistrationStack();
/// ```
/// 中文:打印当前瞬态回调注册位置的堆栈。
///
/// 中文:瞬态帧回调是使用[scheduleFrameCallback]注册的回调。
///
/// 中文:当在调试模式下并在瞬态回调的上下文中调用时,此
/// 中文:函数打印当前瞬态回调注册位置的堆栈跟踪
/// 中文:(即它首次调用[scheduleFrameCallback]的位置)。
///
/// 中文:当在调试模式下在其他上下文中调用时,它打印一条消息,说明
/// 中文:此函数不是在瞬态回调的上下文中调用的。
///
/// 中文:在发布模式下,此函数不执行任何操作。
///
/// 中文:要调用此函数,请使用以下代码:
///
/// ```dart
/// SchedulerBinding.debugPrintTransientCallbackRegistrationStack();
/// ```
static void debugPrintTransientCallbackRegistrationStack() {
assert(() {
if (_FrameCallbackEntry.debugCurrentCallbackStack != null) {
debugPrint('When the current transient callback was registered, this was the stack:');
// 中文:当前瞬态回调注册时,这是堆栈:
debugPrint(
FlutterError.defaultStackFilter(
FlutterError.demangleStackTrace(
_FrameCallbackEntry.debugCurrentCallbackStack!,
).toString().trimRight().split('\n'),
).join('\n'),
);
} else {
debugPrint('No transient callback is currently executing.');
// 中文:当前没有瞬态回调正在执行。
}
return true;
}());
}
final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];
/// Adds a persistent frame callback.
///
/// Persistent callbacks are called after transient
/// (non-persistent) frame callbacks.
///
/// Does *not* request a new frame. Conceptually, persistent frame
/// callbacks are observers of "begin frame" events. Since they are
/// executed after the transient frame callbacks they can drive the
/// rendering pipeline.
///
/// Persistent frame callbacks cannot be unregistered. Once registered, they
/// are called for every frame for the lifetime of the application.
///
/// See also:
///
/// * [WidgetsBinding.drawFrame], which explains the phases of each frame
/// for those apps that use Flutter widgets (and where persistent frame
/// callbacks fit into those phases).
/// 中文:添加持久帧回调。
///
/// 中文:持久回调在瞬态(非持久)帧回调之后调用。
///
/// 中文:*不*请求新帧。从概念上讲,持久帧回调是"开始帧"事件的观察者。
/// 中文:由于它们在瞬态帧回调之后执行,它们可以驱动渲染管道。
///
/// 中文:持久帧回调不能被注销。一旦注册,它们将在应用程序的生命周期内
/// 中文:为每一帧调用。
///
/// 中文:另请参阅:
///
/// 中文: * [WidgetsBinding.drawFrame],它解释了使用Flutter小部件的应用程序
/// 中文: 每帧的各个阶段(以及持久帧回调适合这些阶段的位置)。
void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.add(callback);
}
final List<FrameCallback> _postFrameCallbacks = <FrameCallback>[];
/// Schedule a callback for the end of this frame.
///
/// The provided callback is run immediately after a frame, just after the
/// persistent frame callbacks (which is when the main rendering pipeline has
/// been flushed).
///
/// This method does *not* request a new frame. If a frame is already in
/// progress and the execution of post-frame callbacks has not yet begun, then
/// the registered callback is executed at the end of the current frame.
/// Otherwise, the registered callback is executed after the next frame
/// (whenever that may be, if ever).
///
/// The callbacks are executed in the order in which they have been
/// added.
///
/// Post-frame callbacks cannot be unregistered. They are called exactly once.
///
/// In debug mode, if [debugTracePostFrameCallbacks] is set to true, then the
/// registered callback will show up in the timeline events chart, which can
/// be viewed in [DevTools](https://docs.flutter.dev/tools/devtools).
/// In that case, the `debugLabel` argument specifies the name of the callback
/// as it will appear in the timeline. In profile and release builds,
/// post-frame are never traced, and the `debugLabel` argument is ignored.
///
/// See also:
///
/// * [scheduleFrameCallback], which registers a callback for the start of
/// the next frame.
/// * [WidgetsBinding.drawFrame], which explains the phases of each frame
/// for those apps that use Flutter widgets (and where post frame
/// callbacks fit into those phases).
/// 中文:为此帧的结束调度回调。
///
/// 中文:提供的回调在帧结束后立即运行,就在持久帧回调之后
/// 中文:(这是主渲染管道已被刷新的时候)。
///
/// 中文:此方法*不*请求新帧。如果帧已经在进行中,并且帧后回调的执行
/// 中文:尚未开始,则注册的回调在当前帧结束时执行。
/// 中文:否则,注册的回调在下一帧之后执行(如果有的话,无论何时)。
///
/// 中文:回调按照添加的顺序执行。
///
/// 中文:帧后回调不能被注销。它们只被调用一次。
///
/// 中文:在调试模式下,如果[debugTracePostFrameCallbacks]设置为true,则
/// 中文:注册的回调将显示在时间线事件图表中,可以在
/// 中文:[DevTools](https://docs.flutter.dev/tools/devtools)中查看。
/// 中文:在这种情况下,`debugLabel`参数指定回调在时间线中显示的名称。
/// 中文:在配置文件和发布构建中,帧后回调永远不会被跟踪,并且`debugLabel`参数被忽略。
///
/// 中文:另请参阅:
///
/// 中文: * [scheduleFrameCallback],它为下一帧的开始注册回调。
/// 中文: * [WidgetsBinding.drawFrame],它解释了使用Flutter小部件的应用程序
/// 中文: 每帧的各个阶段(以及帧后回调适合这些阶段的位置)。
void addPostFrameCallback(FrameCallback callback, {String debugLabel = 'callback'}) {
assert(() {
if (debugTracePostFrameCallbacks) {
final FrameCallback originalCallback = callback;
callback = (Duration timeStamp) {
Timeline.startSync(debugLabel);
try {
originalCallback(timeStamp);
} finally {
Timeline.finishSync();
}
};
}
return true;
}());
_postFrameCallbacks.add(callback);
}
csharp
Completer<void>? _nextFrameCompleter;
/// Returns a Future that completes after the frame completes.
///
/// If this is called between frames, a frame is immediately scheduled if
/// necessary. If this is called during a frame, the Future completes after
/// the current frame.
///
/// If the device's screen is currently turned off, this may wait a very long
/// time, since frames are not scheduled while the device's screen is turned
/// off.
/// 中文:返回一个在帧完成后完成的Future。
///
/// 中文:如果这是在帧之间调用的,如果必要,会立即调度一帧。
/// 中文:如果这是在帧期间调用的,Future在当前帧之后完成。
///
/// 中文:如果设备的屏幕当前关闭,这可能会等待很长时间,
/// 中文:因为在设备屏幕关闭时不会调度帧。
Future<void> get endOfFrame {
if (_nextFrameCompleter == null) {
if (schedulerPhase == SchedulerPhase.idle) {
scheduleFrame();
}
_nextFrameCompleter = Completer<void>();
addPostFrameCallback((Duration timeStamp) {
_nextFrameCompleter!.complete();
_nextFrameCompleter = null;
}, debugLabel: 'SchedulerBinding.completeFrame');
}
return _nextFrameCompleter!.future;
}
/// Whether this scheduler has requested that [handleBeginFrame] be called soon.
/// 中文:此调度器是否已请求[handleBeginFrame]很快被调用。
bool get hasScheduledFrame => _hasScheduledFrame;
bool _hasScheduledFrame = false;
/// The phase that the scheduler is currently operating under.
/// 中文:调度器当前运行的阶段。
SchedulerPhase get schedulerPhase => _schedulerPhase;
SchedulerPhase _schedulerPhase = SchedulerPhase.idle;
/// Whether frames are currently being scheduled when [scheduleFrame] is called.
///
/// This value depends on the value of the [lifecycleState].
/// 中文:当调用[scheduleFrame]时,帧当前是否正在被调度。
///
/// 中文:此值取决于[lifecycleState]的值。
bool get framesEnabled => _framesEnabled;
bool _framesEnabled = true;
void _setFramesEnabledState(bool enabled) {
if (_framesEnabled == enabled) {
return;
}
_framesEnabled = enabled;
if (enabled) {
scheduleFrame();
}
}
/// Ensures callbacks for [PlatformDispatcher.onBeginFrame] and
/// [PlatformDispatcher.onDrawFrame] are registered.
/// 中文:确保[PlatformDispatcher.onBeginFrame]和
/// 中文:[PlatformDispatcher.onDrawFrame]的回调已注册。
@protected
void ensureFrameCallbacksRegistered() {
platformDispatcher.onBeginFrame ??= _handleBeginFrame;
platformDispatcher.onDrawFrame ??= _handleDrawFrame;
}
/// Schedules a new frame using [scheduleFrame] if this object is not
/// currently producing a frame.
///
/// Calling this method ensures that [handleDrawFrame] will eventually be
/// called, unless it's already in progress.
///
/// This has no effect if [schedulerPhase] is
/// [SchedulerPhase.transientCallbacks] or [SchedulerPhase.midFrameMicrotasks]
/// (because a frame is already being prepared in that case), or
/// [SchedulerPhase.persistentCallbacks] (because a frame is actively being
/// rendered in that case). It will schedule a frame if the [schedulerPhase]
/// is [SchedulerPhase.idle] (in between frames) or
/// [SchedulerPhase.postFrameCallbacks] (after a frame).
/// 中文:如果此对象当前没有产生帧,则使用[scheduleFrame]调度新帧。
///
/// 中文:调用此方法确保[handleDrawFrame]最终会被调用,除非它已经在进行中。
///
/// 中文:如果[schedulerPhase]是[SchedulerPhase.transientCallbacks]或
/// 中文:[SchedulerPhase.midFrameMicrotasks](因为在这种情况下帧已经在准备中),
/// 中文:或[SchedulerPhase.persistentCallbacks](因为在这种情况下帧正在积极
/// 中文:被渲染),则此方法没有效果。如果[schedulerPhase]是[SchedulerPhase.idle]
/// 中文:(在帧之间)或[SchedulerPhase.postFrameCallbacks](在帧之后),
/// 中文:它将调度一帧。
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
scss
/// If necessary, schedules a new frame by calling
/// [dart:ui.PlatformDispatcher.scheduleFrame].
///
/// After this is called, the engine will (eventually) call
/// [handleBeginFrame]. (This call might be delayed, e.g. if the device's
/// screen is turned off it will typically be delayed until the screen is on
/// and the application is visible.) Calling this during a frame forces
/// another frame to be scheduled, even if the current frame has not yet
/// completed.
///
/// Scheduled frames are serviced when triggered by a "Vsync" signal provided
/// by the operating system. The "Vsync" signal, or vertical synchronization
/// signal, was historically related to the display refresh, at a time when
/// hardware physically moved a beam of electrons vertically between updates
/// of the display. The operation of contemporary hardware is somewhat more
/// subtle and complicated, but the conceptual "Vsync" refresh signal continue
/// to be used to indicate when applications should update their rendering.
///
/// To have a stack trace printed to the console any time this function
/// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
///
/// See also:
///
/// * [scheduleForcedFrame], which ignores the [lifecycleState] when
/// scheduling a frame.
/// * [scheduleWarmUpFrame], which ignores the "Vsync" signal entirely and
/// triggers a frame immediately.
/// 中文:如有必要,通过调用[dart:ui.PlatformDispatcher.scheduleFrame]调度新帧。
///
/// 中文:调用此方法后,引擎将(最终)调用[handleBeginFrame]。
/// 中文:(此调用可能会延迟,例如,如果设备的屏幕关闭,通常会延迟到屏幕打开
/// 中文:并且应用程序可见时。)在帧期间调用此方法会强制调度另一帧,
/// 中文:即使当前帧尚未完成。
///
/// 中文:调度的帧在由操作系统提供的"Vsync"信号触发时被服务。
/// 中文:"Vsync"信号,或垂直同步信号,在历史上与显示刷新相关,
/// 中文:在硬件物理上在显示更新之间垂直移动电子束的时候。
/// 中文:现代硬件的操作有些更微妙和复杂,但概念上的"Vsync"刷新信号
/// 中文:继续用于指示应用程序应该何时更新其渲染。
///
/// 中文:要在此函数调度帧时将堆栈跟踪打印到控制台,
/// 中文:请将[debugPrintScheduleFrameStacks]设置为true。
///
/// 中文:另请参阅:
///
/// 中文: * [scheduleForcedFrame],它在调度帧时忽略[lifecycleState]。
/// 中文: * [scheduleWarmUpFrame],它完全忽略"Vsync"信号并立即触发帧。
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) {
return;
}
assert(() {
if (debugPrintScheduleFrameStacks) {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
}
return true;
}());
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
/// Schedules a new frame by calling
/// [dart:ui.PlatformDispatcher.scheduleFrame].
///
/// After this is called, the engine will call [handleBeginFrame], even if
/// frames would normally not be scheduled by [scheduleFrame] (e.g. even if
/// the device's screen is turned off).
///
/// The framework uses this to force a frame to be rendered at the correct
/// size when the phone is rotated, so that a correctly-sized rendering is
/// available when the screen is turned back on.
///
/// To have a stack trace printed to the console any time this function
/// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
///
/// Prefer using [scheduleFrame] unless it is imperative that a frame be
/// scheduled immediately, since using [scheduleForcedFrame] will cause
/// significantly higher battery usage when the device should be idle.
///
/// Consider using [scheduleWarmUpFrame] instead if the goal is to update the
/// rendering as soon as possible (e.g. at application startup).
/// 中文:通过调用[dart:ui.PlatformDispatcher.scheduleFrame]调度新帧。
///
/// 中文:调用此方法后,引擎将调用[handleBeginFrame],即使帧通常不会
/// 中文:由[scheduleFrame]调度(例如,即使设备的屏幕关闭)。
///
/// 中文:框架使用这个来强制在手机旋转时以正确的尺寸渲染帧,
/// 中文:以便在屏幕重新打开时有正确尺寸的渲染可用。
///
/// 中文:要在此函数调度帧时将堆栈跟踪打印到控制台,
/// 中文:请将[debugPrintScheduleFrameStacks]设置为true。
///
/// 中文:除非必须立即调度帧,否则优先使用[scheduleFrame],
/// 中文:因为使用[scheduleForcedFrame]会在设备应该空闲时
/// 中文:导致明显更高的电池使用率。
///
/// 中文:如果目标是尽快更新渲染(例如在应用程序启动时),
/// 中文:请考虑使用[scheduleWarmUpFrame]。
void scheduleForcedFrame() {
if (_hasScheduledFrame) {
return;
}
assert(() {
if (debugPrintScheduleFrameStacks) {
debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
}
return true;
}());
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
scss
/// If necessary, schedules a new frame by calling
/// [dart:ui.PlatformDispatcher.scheduleFrame].
///
/// After this is called, the engine will (eventually) call
/// [handleBeginFrame]. (This call might be delayed, e.g. if the device's
/// screen is turned off it will typically be delayed until the screen is on
/// and the application is visible.) Calling this during a frame forces
/// another frame to be scheduled, even if the current frame has not yet
/// completed.
///
/// Scheduled frames are serviced when triggered by a "Vsync" signal provided
/// by the operating system. The "Vsync" signal, or vertical synchronization
/// signal, was historically related to the display refresh, at a time when
/// hardware physically moved a beam of electrons vertically between updates
/// of the display. The operation of contemporary hardware is somewhat more
/// subtle and complicated, but the conceptual "Vsync" refresh signal continue
/// to be used to indicate when applications should update their rendering.
///
/// To have a stack trace printed to the console any time this function
/// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
///
/// See also:
///
/// * [scheduleForcedFrame], which ignores the [lifecycleState] when
/// scheduling a frame.
/// * [scheduleWarmUpFrame], which ignores the "Vsync" signal entirely and
/// triggers a frame immediately.
/// 中文:如有必要,通过调用[dart:ui.PlatformDispatcher.scheduleFrame]调度新帧。
///
/// 中文:调用此方法后,引擎将(最终)调用[handleBeginFrame]。
/// 中文:(此调用可能会延迟,例如,如果设备的屏幕关闭,通常会延迟到屏幕打开
/// 中文:并且应用程序可见时。)在帧期间调用此方法会强制调度另一帧,
/// 中文:即使当前帧尚未完成。
///
/// 中文:调度的帧在由操作系统提供的"Vsync"信号触发时被服务。
/// 中文:"Vsync"信号,或垂直同步信号,在历史上与显示刷新相关,
/// 中文:在硬件物理上在显示更新之间垂直移动电子束的时候。
/// 中文:现代硬件的操作有些更微妙和复杂,但概念上的"Vsync"刷新信号
/// 中文:继续用于指示应用程序应该何时更新其渲染。
///
/// 中文:要在此函数调度帧时将堆栈跟踪打印到控制台,
/// 中文:请将[debugPrintScheduleFrameStacks]设置为true。
///
/// 中文:另请参阅:
///
/// 中文: * [scheduleForcedFrame],它在调度帧时忽略[lifecycleState]。
/// 中文: * [scheduleWarmUpFrame],它完全忽略"Vsync"信号并立即触发帧。
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) {
return;
}
assert(() {
if (debugPrintScheduleFrameStacks) {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
}
return true;
}());
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
/// Schedules a new frame by calling
/// [dart:ui.PlatformDispatcher.scheduleFrame].
///
/// After this is called, the engine will call [handleBeginFrame], even if
/// frames would normally not be scheduled by [scheduleFrame] (e.g. even if
/// the device's screen is turned off).
///
/// The framework uses this to force a frame to be rendered at the correct
/// size when the phone is rotated, so that a correctly-sized rendering is
/// available when the screen is turned back on.
///
/// To have a stack trace printed to the console any time this function
/// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
///
/// Prefer using [scheduleFrame] unless it is imperative that a frame be
/// scheduled immediately, since using [scheduleForcedFrame] will cause
/// significantly higher battery usage when the device should be idle.
///
/// Consider using [scheduleWarmUpFrame] instead if the goal is to update the
/// rendering as soon as possible (e.g. at application startup).
/// 中文:通过调用[dart:ui.PlatformDispatcher.scheduleFrame]调度新帧。
///
/// 中文:调用此方法后,引擎将调用[handleBeginFrame],即使帧通常不会
/// 中文:由[scheduleFrame]调度(例如,即使设备的屏幕关闭)。
///
/// 中文:框架使用这个来强制在手机旋转时以正确的尺寸渲染帧,
/// 中文:以便在屏幕重新打开时有正确尺寸的渲染可用。
///
/// 中文:要在此函数调度帧时将堆栈跟踪打印到控制台,
/// 中文:请将[debugPrintScheduleFrameStacks]设置为true。
///
/// 中文:除非必须立即调度帧,否则优先使用[scheduleFrame],
/// 中文:因为使用[scheduleForcedFrame]会在设备应该空闲时
/// 中文:导致明显更高的电池使用率。
///
/// 中文:如果目标是尽快更新渲染(例如在应用程序启动时),
/// 中文:请考虑使用[scheduleWarmUpFrame]。
void scheduleForcedFrame() {
if (_hasScheduledFrame) {
return;
}
assert(() {
if (debugPrintScheduleFrameStacks) {
debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
}
return true;
}());
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
scss
bool _warmUpFrame = false;
/// Schedule a frame to run as soon as possible, rather than waiting for
/// the engine to request a frame in response to a system "Vsync" signal.
///
/// This is used during application startup so that the first frame (which is
/// likely to be quite expensive) gets a few extra milliseconds to run.
///
/// Locks events dispatching until the scheduled frame has completed.
///
/// If a frame has already been scheduled with [scheduleFrame] or
/// [scheduleForcedFrame], this call may delay that frame.
///
/// If any scheduled frame has already begun or if another
/// [scheduleWarmUpFrame] was already called, this call will be ignored.
///
/// Prefer [scheduleFrame] to update the display in normal operation.
///
/// ## Design discussion
///
/// The Flutter engine prompts the framework to generate frames when it
/// receives a request from the operating system (known for historical reasons
/// as a vsync). However, this may not happen for several milliseconds after
/// the app starts (or after a hot reload). To make use of the time between
/// when the widget tree is first configured and when the engine requests an
/// update, the framework schedules a _warm-up frame_.
///
/// A warm-up frame may never actually render (as the engine did not request
/// it and therefore does not have a valid context in which to paint), but it
/// will cause the framework to go through the steps of building, laying out,
/// and painting, which can together take several milliseconds. Thus, when the
/// engine requests a real frame, much of the work will already have been
/// completed, and the framework can generate the frame with minimal
/// additional effort.
///
/// Warm-up frames are scheduled by [runApp] on startup, and by
/// [RendererBinding.performReassemble] during a hot reload.
///
/// Warm-up frames are also scheduled when the framework is unblocked by a
/// call to [RendererBinding.allowFirstFrame] (corresponding to a call to
/// [RendererBinding.deferFirstFrame] that blocked the rendering).
/// 中文:调度一个尽快运行的帧,而不是等待引擎响应系统"Vsync"信号请求帧。
///
/// 中文:这在应用程序启动期间使用,以便第一帧(可能相当昂贵)
/// 中文:获得几毫秒的额外运行时间。
///
/// 中文:锁定事件分派,直到调度的帧完成。
///
/// 中文:如果已经使用[scheduleFrame]或[scheduleForcedFrame]调度了帧,
/// 中文:此调用可能会延迟该帧。
///
/// 中文:如果任何调度的帧已经开始,或者另一个[scheduleWarmUpFrame]
/// 中文:已经被调用,此调用将被忽略。
///
/// 中文:在正常操作中,优先使用[scheduleFrame]更新显示。
///
/// 中文:## 设计讨论
///
/// 中文:Flutter引擎在收到来自操作系统的请求(由于历史原因称为vsync)时,
/// 中文:提示框架生成帧。然而,这可能在应用程序启动(或热重载)后
/// 中文:几毫秒内不会发生。为了利用小部件树首次配置和引擎请求
/// 中文:更新之间的时间,框架调度了一个_预热帧_。
///
/// 中文:预热帧可能永远不会真正渲染(因为引擎没有请求它,
/// 中文:因此没有有效的上下文进行绘制),但它会使框架经历构建、布局
/// 中文:和绘制的步骤,这些步骤加起来可能需要几毫秒。因此,当引擎
/// 中文:请求真实帧时,大部分工作已经完成,框架可以以最小的
/// 中文:额外努力生成帧。
///
/// 中文:预热帧由[runApp]在启动时调度,并由
/// 中文:[RendererBinding.performReassemble]在热重载期间调度。
///
/// 中文:当框架被调用[RendererBinding.allowFirstFrame]解除阻塞时
/// 中文:(对应于调用[RendererBinding.deferFirstFrame]阻塞渲染),
/// 中文:也会调度预热帧。
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) {
return;
}
_warmUpFrame = true;
TimelineTask? debugTimelineTask;
if (!kReleaseMode) {
debugTimelineTask = TimelineTask()..start('Warm-up frame');
}
final bool hadScheduledFrame = _hasScheduledFrame;
PlatformDispatcher.instance.scheduleWarmUpFrame(
beginFrame: () {
assert(_warmUpFrame);
handleBeginFrame(null);
},
drawFrame: () {
assert(_warmUpFrame);
handleDrawFrame();
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
// 中文:我们在此帧之后调用resetEpoch,以便在热重载的情况下,
// 中文:下一帧假装在此预热帧之后立即发生。预热帧的时间戳通常
// 中文:会远在过去(上一个真实帧的时间),所以如果我们不重置纪元,
// 中文:我们会看到从预热帧中的旧时间到"真实"帧中的新时间的突然跳跃。
// 中文:最大的问题是隐式动画最终在旧时间被触发,然后跳过每一帧
// 中文:并在新时间完成。
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame) {
scheduleFrame();
}
},
);
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
// 中文:锁定事件,以便触摸事件等不会插入,直到调度的帧完成。
lockEvents(() async {
await endOfFrame;
if (!kReleaseMode) {
debugTimelineTask!.finish();
}
});
}
java
Duration? _firstRawTimeStampInEpoch;
Duration _epochStart = Duration.zero;
Duration _lastRawTimeStamp = Duration.zero;
/// Prepares the scheduler for a non-monotonic change to how time stamps are
/// calculated.
///
/// Callbacks received from the scheduler assume that their time stamps are
/// monotonically increasing. The raw time stamp passed to [handleBeginFrame]
/// is monotonic, but the scheduler might adjust those time stamps to provide
/// [timeDilation]. Without careful handling, these adjusts could cause time
/// to appear to run backwards.
///
/// The [resetEpoch] function ensures that the time stamps are monotonic by
/// resetting the base time stamp used for future time stamp adjustments to the
/// current value. For example, if the [timeDilation] decreases, rather than
/// scaling down the [Duration] since the beginning of time, [resetEpoch] will
/// ensure that we only scale down the duration since [resetEpoch] was called.
///
/// Setting [timeDilation] calls [resetEpoch] automatically. You don't need to
/// call [resetEpoch] yourself.
/// 中文:准备调度器进行时间戳计算方式的非单调变化。
///
/// 中文:从调度器接收的回调假设它们的时间戳是单调递增的。
/// 中文:传递给[handleBeginFrame]的原始时间戳是单调的,但调度器
/// 中文:可能会调整这些时间戳以提供[timeDilation]。如果不仔细处理,
/// 中文:这些调整可能导致时间看起来向后运行。
///
/// 中文:[resetEpoch]函数通过重置用于未来时间戳调整的基础时间戳
/// 中文:为当前值,确保时间戳是单调的。例如,如果[timeDilation]减小,
/// 中文:[resetEpoch]将确保我们只缩小自[resetEpoch]被调用以来的持续时间,
/// 中文:而不是从时间开始以来的[Duration]。
///
/// 中文:设置[timeDilation]会自动调用[resetEpoch]。你不需要
/// 中文:自己调用[resetEpoch]。
void resetEpoch() {
_epochStart = _adjustForEpoch(_lastRawTimeStamp);
_firstRawTimeStampInEpoch = null;
}
/// Adjusts the given time stamp into the current epoch.
///
/// This both offsets the time stamp to account for when the epoch started
/// (both in raw time and in the epoch's own time line) and scales the time
/// stamp to reflect the time dilation in the current epoch.
///
/// These mechanisms together combine to ensure that the durations we give
/// during frame callbacks are monotonically increasing.
/// 中文:将给定的时间戳调整到当前纪元。
///
/// 中文:这既偏移时间戳以考虑纪元开始的时间
/// 中文:(在原始时间和纪元自己的时间线中),又缩放时间戳
/// 中文:以反映当前纪元中的时间膨胀。
///
/// 中文:这些机制共同确保我们在帧回调期间给出的持续时间是单调递增的。
Duration _adjustForEpoch(Duration rawTimeStamp) {
final Duration rawDurationSinceEpoch = _firstRawTimeStampInEpoch == null ? Duration.zero : rawTimeStamp - _firstRawTimeStampInEpoch!;
return Duration(microseconds: (rawDurationSinceEpoch.inMicroseconds / timeDilation).round() + _epochStart.inMicroseconds);
}
/// The time stamp for the frame currently being processed.
///
/// This is only valid while between the start of [handleBeginFrame] and the
/// end of the corresponding [handleDrawFrame], i.e. while a frame is being
/// produced.
/// 中文:当前正在处理的帧的时间戳。
///
/// 中文:这仅在[handleBeginFrame]的开始和相应的[handleDrawFrame]的
/// 中文:结束之间有效,即在产生帧时。
Duration get currentFrameTimeStamp {
assert(_currentFrameTimeStamp != null);
return _currentFrameTimeStamp!;
}
Duration? _currentFrameTimeStamp;
/// The raw time stamp as provided by the engine to
/// [dart:ui.PlatformDispatcher.onBeginFrame] for the frame currently being
/// processed.
///
/// Unlike [currentFrameTimeStamp], this time stamp is neither adjusted to
/// offset when the epoch started nor scaled to reflect the [timeDilation] in
/// the current epoch.
///
/// On most platforms, this is a more or less arbitrary value, and should
/// generally be ignored. On Fuchsia, this corresponds to the system-provided
/// presentation time, and can be used to ensure that animations running in
/// different processes are synchronized.
/// 中文:引擎提供给[dart:ui.PlatformDispatcher.onBeginFrame]的
/// 中文:当前正在处理的帧的原始时间戳。
///
/// 中文:与[currentFrameTimeStamp]不同,此时间戳既不调整为
/// 中文:偏移纪元开始的时间,也不缩放以反映当前纪元中的[timeDilation]。
///
/// 中文:在大多数平台上,这是一个或多或少的任意值,通常应该被忽略。
/// 中文:在Fuchsia上,这对应于系统提供的呈现时间,可用于确保
/// 中文:在不同进程中运行的动画是同步的。
Duration get currentSystemFrameTimeStamp {
return _lastRawTimeStamp;
}
int _debugFrameNumber = 0;
String? _debugBanner;
// Whether the current engine frame needs to be postponed till after the
// warm-up frame.
//
// Engine may begin a frame in the middle of the warm-up frame because the
// warm-up frame is scheduled by timers while the engine frame is scheduled
// by platform specific frame scheduler (e.g. `requestAnimationFrame` on the
// web). When this happens, we let the warm-up frame finish, and postpone the
// engine frame.
// 中文:当前引擎帧是否需要推迟到预热帧之后。
//
// 中文:引擎可能在预热帧中间开始一帧,因为预热帧是由计时器调度的,
// 中文:而引擎帧是由平台特定的帧调度器调度的(例如,在网络上的
// 中文:`requestAnimationFrame`)。当这种情况发生时,我们让预热帧
// 中文:完成,并推迟引擎帧。
bool _rescheduleAfterWarmUpFrame = false;
void _handleBeginFrame(Duration rawTimeStamp) {
if (_warmUpFrame) {
// "begin frame" and "draw frame" must strictly alternate. Therefore
// _rescheduleAfterWarmUpFrame cannot possibly be true here as it is
// reset by _handleDrawFrame.
// 中文:"begin frame"和"draw frame"必须严格交替。因此
// 中文:_rescheduleAfterWarmUpFrame不可能在这里为true,因为它
// 中文:被_handleDrawFrame重置。
assert(!_rescheduleAfterWarmUpFrame);
_rescheduleAfterWarmUpFrame = true;
return;
}
handleBeginFrame(rawTimeStamp);
}
void _handleDrawFrame() {
if (_rescheduleAfterWarmUpFrame) {
_rescheduleAfterWarmUpFrame = false;
// Reschedule in a post-frame callback to allow the draw-frame phase of
// the warm-up frame to finish.
// 中文:在帧后回调中重新调度,以允许预热帧的draw-frame阶段完成。
addPostFrameCallback((Duration timeStamp) {
// Force an engine frame.
//
// We need to reset _hasScheduledFrame here because we cancelled the
// original engine frame, and therefore did not run handleBeginFrame
// who is responsible for resetting it. So if a frame callback set this
// to true in the "begin frame" part of the warm-up frame, it will
// still be true here and cause us to skip scheduling an engine frame.
// 中文:强制引擎帧。
//
// 中文:我们需要在这里重置_hasScheduledFrame,因为我们取消了
// 中文:原始引擎帧,因此没有运行负责重置它的handleBeginFrame。
// 中文:所以如果帧回调在预热帧的"begin frame"部分将其设置为true,
// 中文:它在这里仍然为true,并导致我们跳过调度引擎帧。
_hasScheduledFrame = false;
scheduleFrame();
}, debugLabel: 'SchedulerBinding.scheduleFrame');
return;
}
handleDrawFrame();
}
final TimelineTask? _frameTimelineTask = kReleaseMode ? null : TimelineTask();
scss
/// Called by the engine to prepare the framework to produce a new frame.
///
/// This function calls all the transient frame callbacks registered by
/// [scheduleFrameCallback]. It then returns, any scheduled microtasks are run
/// (e.g. handlers for any [Future]s resolved by transient frame callbacks),
/// and [handleDrawFrame] is called to continue the frame.
///
/// If the given time stamp is null, the time stamp from the last frame is
/// reused.
///
/// To have a banner shown at the start of every frame in debug mode, set
/// [debugPrintBeginFrameBanner] to true. The banner will be printed to the
/// console using [debugPrint] and will contain the frame number (which
/// increments by one for each frame), and the time stamp of the frame. If the
/// given time stamp was null, then the string "warm-up frame" is shown
/// instead of the time stamp. This allows frames eagerly pushed by the
/// framework to be distinguished from those requested by the engine in
/// response to the "Vsync" signal from the operating system.
///
/// You can also show a banner at the end of every frame by setting
/// [debugPrintEndFrameBanner] to true. This allows you to distinguish log
/// statements printed during a frame from those printed between frames (e.g.
/// in response to events or timers).
/// 中文:由引擎调用,准备框架产生新帧。
///
/// 中文:此函数调用由[scheduleFrameCallback]注册的所有瞬态帧回调。
/// 中文:然后它返回,运行任何调度的微任务(例如,由瞬态帧回调解析的
/// 中文:任何[Future]的处理程序),并调用[handleDrawFrame]继续帧。
///
/// 中文:如果给定的时间戳为null,则重用上一帧的时间戳。
///
/// 中文:要在调试模式下在每帧开始时显示横幅,请设置
/// 中文:[debugPrintBeginFrameBanner]为true。横幅将使用[debugPrint]
/// 中文:打印到控制台,并将包含帧号(每帧递增一个)和帧的时间戳。
/// 中文:如果给定的时间戳为null,则显示字符串"warm-up frame"
/// 中文:而不是时间戳。这允许区分框架急切推送的帧和引擎响应
/// 中文:操作系统的"Vsync"信号请求的帧。
///
/// 中文:你也可以通过设置[debugPrintEndFrameBanner]为true在每帧结束时
/// 中文:显示横幅。这允许你区分在帧期间打印的日志语句和在帧之间
/// 中文:打印的日志语句(例如,响应事件或计时器)。
void handleBeginFrame(Duration? rawTimeStamp) {
_frameTimelineTask?.start('Frame');
_firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null) {
_lastRawTimeStamp = rawTimeStamp;
}
assert(() {
_debugFrameNumber += 1;
if (debugPrintBeginFrameBanner || debugPrintEndFrameBanner) {
final StringBuffer frameTimeStampDescription = StringBuffer();
if (rawTimeStamp != null) {
_debugDescribeTimeStamp(_currentFrameTimeStamp!, frameTimeStampDescription);
} else {
frameTimeStampDescription.write('(warm-up frame)');
}
_debugBanner = '▄▄▄▄▄▄▄▄ Frame ${_debugFrameNumber.toString().padRight(7)} ${frameTimeStampDescription.toString().padLeft(18)} ▄▄▄▄▄▄▄▄';
if (debugPrintBeginFrameBanner) {
debugPrint(_debugBanner);
}
}
return true;
}());
assert(schedulerPhase == SchedulerPhase.idle);
_hasScheduledFrame = false;
try {
// TRANSIENT FRAME CALLBACKS
// 中文:瞬态帧回调
_frameTimelineTask?.start('Animate');
_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;
}
}
DartPerformanceMode? _performanceMode;
int _numPerformanceModeRequests = 0;
/// Request a specific [DartPerformanceMode].
///
/// Returns `null` if the request was not successful due to conflicting performance mode requests.
/// Two requests are said to be in conflict if they are not of the same [DartPerformanceMode] type,
/// and an explicit request for a performance mode has been made prior.
///
/// Requestor is responsible for calling [PerformanceModeRequestHandle.dispose] when it no longer
/// requires the performance mode.
/// 中文:请求特定的[DartPerformanceMode]。
///
/// 中文:如果由于冲突的性能模式请求而导致请求不成功,则返回`null`。
/// 中文:如果两个请求不是相同的[DartPerformanceMode]类型,
/// 中文:并且之前已经明确请求了性能模式,则称它们冲突。
///
/// 中文:请求者负责在不再需要性能模式时调用[PerformanceModeRequestHandle.dispose]。
PerformanceModeRequestHandle? requestPerformanceMode(DartPerformanceMode mode) {
// conflicting requests are not allowed.
// 中文:不允许冲突的请求。
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);
}
/// Remove a request for a specific [DartPerformanceMode].
///
/// If all the pending requests have been disposed, the engine will revert to the
/// [DartPerformanceMode.balanced] performance mode.
/// 中文:删除对特定[DartPerformanceMode]的请求。
///
/// 中文:如果所有待处理的请求都已处置,引擎将恢复为
/// 中文:[DartPerformanceMode.balanced]性能模式。
void _disposePerformanceModeRequest() {
_numPerformanceModeRequests--;
if (_numPerformanceModeRequests == 0) {
_performanceMode = null;
PlatformDispatcher.instance.requestDartPerformanceMode(DartPerformanceMode.balanced);
}
}
scss
/// Returns the current [DartPerformanceMode] requested or `null` if no requests have
/// been made.
///
/// This is only supported in debug and profile modes, returns `null` in release mode.
/// 中文:返回当前请求的[DartPerformanceMode],如果没有请求,则返回`null`。
///
/// 中文:这仅在调试和配置文件模式下支持,在发布模式下返回`null`。
DartPerformanceMode? debugGetRequestedPerformanceMode() {
if (!(kDebugMode || kProfileMode)) {
return null;
} else {
return _performanceMode;
}
}
/// Called by the engine to produce a new frame.
///
/// This method is called immediately after [handleBeginFrame]. It calls all
/// the callbacks registered by [addPersistentFrameCallback], which typically
/// drive the rendering pipeline, and then calls the callbacks registered by
/// [addPostFrameCallback].
///
/// See [handleBeginFrame] for a discussion about debugging hooks that may be
/// useful when working with frame callbacks.
/// 中文:由引擎调用以产生新帧。
///
/// 中文:此方法在[handleBeginFrame]之后立即调用。它调用所有
/// 中文:由[addPersistentFrameCallback]注册的回调,这些回调通常
/// 中文:驱动渲染管道,然后调用由[addPostFrameCallback]注册的回调。
///
/// 中文:有关处理帧回调时可能有用的调试钩子的讨论,请参见[handleBeginFrame]。
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
_frameTimelineTask?.finish(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS
// 中文:持久帧回调
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in List<FrameCallback>.of(_persistentCallbacks)) {
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
}
// POST-FRAME CALLBACKS
// 中文:帧后回调
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.of(_postFrameCallbacks);
_postFrameCallbacks.clear();
if (!kReleaseMode) {
FlutterTimeline.startSync('POST_FRAME');
}
try {
for (final FrameCallback callback in localPostFrameCallbacks) {
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
}
} finally {
if (!kReleaseMode) {
FlutterTimeline.finishSync();
}
}
} finally {
_schedulerPhase = SchedulerPhase.idle;
_frameTimelineTask?.finish(); // end the Frame
assert(() {
if (debugPrintEndFrameBanner) {
debugPrint('▀' * _debugBanner!.length);
}
_debugBanner = null;
return true;
}());
_currentFrameTimeStamp = null;
}
}
void _profileFramePostEvent(FrameTiming frameTiming) {
postEvent('Flutter.Frame', <String, dynamic>{
'number': frameTiming.frameNumber,
'startTime': frameTiming.timestampInMicroseconds(FramePhase.buildStart),
'elapsed': frameTiming.totalSpan.inMicroseconds,
'build': frameTiming.buildDuration.inMicroseconds,
'raster': frameTiming.rasterDuration.inMicroseconds,
'vsyncOverhead': frameTiming.vsyncOverhead.inMicroseconds,
});
}
static void _debugDescribeTimeStamp(Duration timeStamp, StringBuffer buffer) {
if (timeStamp.inDays > 0) {
buffer.write('${timeStamp.inDays}d ');
}
if (timeStamp.inHours > 0) {
buffer.write('${timeStamp.inHours - timeStamp.inDays * Duration.hoursPerDay}h ');
}
if (timeStamp.inMinutes > 0) {
buffer.write('${timeStamp.inMinutes - timeStamp.inHours * Duration.minutesPerHour}m ');
}
if (timeStamp.inSeconds > 0) {
buffer.write('${timeStamp.inSeconds - timeStamp.inMinutes * Duration.secondsPerMinute}s ');
}
buffer.write('${timeStamp.inMilliseconds - timeStamp.inSeconds * Duration.millisecondsPerSecond}');
final int microseconds = timeStamp.inMicroseconds - timeStamp.inMilliseconds * Duration.microsecondsPerMillisecond;
if (microseconds > 0) {
buffer.write('.${microseconds.toString().padLeft(3, "0")}');
}
buffer.write('ms');
}
dart
// Calls the given [callback] with [timestamp] as argument.
//
// Wraps the callback in a try/catch and forwards any error to
// [debugSchedulerExceptionHandler], if set. If not set, prints
// the error.
// 中文:使用[timestamp]作为参数调用给定的[callback]。
//
// 中文:将回调包装在try/catch中,并将任何错误转发给
// 中文:[debugSchedulerExceptionHandler](如果设置)。如果未设置,则打印错误。
@pragma('vm:notify-debugger-on-exception')
void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace? callbackStack ]) {
assert(_FrameCallbackEntry.debugCurrentCallbackStack == null);
assert(() {
_FrameCallbackEntry.debugCurrentCallbackStack = callbackStack;
return true;
}());
try {
callback(timeStamp);
} catch (exception, exceptionStack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: exceptionStack,
library: 'scheduler library',
context: ErrorDescription('during a scheduler callback'),
// 中文:在调度器回调期间
informationCollector: (callbackStack == null) ? null : () {
return <DiagnosticsNode>[
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',
// 中文:\n这个异常是在调度器回调的上下文中抛出的。
// 中文:当调度器回调被_注册_(而不是异常被抛出)时,这是堆栈
callbackStack,
),
];
},
));
}
assert(() {
_FrameCallbackEntry.debugCurrentCallbackStack = null;
return true;
}());
}
}
/// The default [SchedulingStrategy] for [SchedulerBinding.schedulingStrategy].
///
/// If there are any frame callbacks registered, only runs tasks with
/// a [Priority] of [Priority.animation] or higher. Otherwise, runs
/// all tasks.
/// 中文:[SchedulerBinding.schedulingStrategy]的默认[SchedulingStrategy]。
///
/// 中文:如果有任何帧回调注册,则只运行[Priority]为[Priority.animation]
/// 中文:或更高的任务。否则,运行所有任务。
bool defaultSchedulingStrategy({ required int priority, required SchedulerBinding scheduler }) {
if (scheduler.transientCallbackCount > 0) {
return priority >= Priority.animation.value;
}
return true;
}
这个类是 Flutter 框架的核心部分,负责管理帧调度、任务调度和时间处理等关键功能。