0%

Flutter-runApp()方法

Flutter程序的入口main()方法会调用runApp()方法,我们本篇探索runApp都做了啥。

概览

1
2
3
4
5
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}

这个方法一看很简洁,一共调用了WidgetsFlutterBinding的三个方法,在看三个方法的实现之前,我们看一下WidgetsFlutterBinding是什么。

WidgetsFlutterBinding

我们看一下官方的解释:

A concrete binding for applications based on the Widgets framework.This is the glue that binds the framework to the Flutter engine.

翻译过来就是,一个基于Widgets framework的应用程序的具体绑定,它是绑定frameworkFlutter engine的胶水层。

1
2
3
4
5
6
7
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}

它的父类BindingBase是一个抽象类,with实现了很多mixin,这些mixin只能用于继承自BindingBase的类。mixin的作用是扩展功能,mixin可以类比于iOSprotocol(个人见解,如果不对欢迎指正)。

BindingBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
abstract class BindingBase {
/// 省略代码
/// However, multiple window support is not yet implemented, so currently this
/// provides access to the one and only window.
// TODO(gspencergoog): remove the preceding note once multi-window support is
// active.
// 唯一的window
ui.SingletonFlutterWindow get window => ui.window;
/// 每一个BindingBase类定义行为 都有一个 platformDispatcher 作为回调(handlers)
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
/// 初始化实例
void initInstances() {
assert(!_debugInitialized);
assert(() {
_debugInitialized = true;
return true;
}());
}
/// The current [WidgetsBinding], if one has been created.
/// ensureInitialized方法返回的实例
/// If you need the binding to be constructed before calling [runApp],
/// you can ensure a Widget binding has been constructed by calling the
/// `WidgetsFlutterBinding.ensureInitialized()` function.
static WidgetsBinding? get instance => _instance;
static WidgetsBinding? _instance;
/// 注册 service extensions 初始化之后调用
void initServiceExtensions() {
///省略代码
}
}
  • ui.window:是Flutter App显示的窗口,它继承自FlutterView,位于Flutter engine层。
  • ui.PlatformDispatcher.instance:platformDispatcher是Flutter 的一个事件分发器,负责Flutter分发engine的事件,和传递事件给engine层。
  • initInstances:初始化实例的方法。
  • initServiceExtensions():注册 service extensions,比如platformOverrideactiveDevToolsServerAddress等。

ensureInitialized()方法

该方法的作用是返回一个WidgetsBinding类型实例,如果未创建就新创建一个。

1
2
3
4
5
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}

就是返回一个WidgetsBinding.instance实例,因为WidgetsFlutterBinding实现了GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBindingmixin,mixin的initInstancesinitServiceExtensions方法也会调用,每个mixin的功能:

  • GestureBinding:处理手势。
  • SchedulerBinding: 处理系统调度。
  • ServicesBinding:处理与原生的交互。
  • PaintingBinding:处理绘制。
  • SemanticsBinding:处理语义化。
  • RendererBinding:处理渲染。
  • WidgetsBindingWidgets相关。

我们下面主要看WidgetsBindingRendererBinding

WidgetsBinding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
assert(() {
_debugAddStackFilters();
return true;
}());
// Initialization of [_buildOwner] has to be done after
// [super.initInstances] is called, as it requires [ServicesBinding] to
// properly setup the [defaultBinaryMessenger] instance.
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
///省略代码
}

WidgetsBinding初始化会创建一个BuildOwner对象,它的作用是管理Widget树和Element树。

RendererBinding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
if (kIsWeb) {
addPostFrameCallback(_handleWebFirstFrame);
}
}

/// The current [RendererBinding], if one has been created.
static RendererBinding? get instance => _instance;
static RendererBinding? _instance;
///省略代码
}

RendererBinding初始化会创建一个PipelineOwner对象,用于管理RenderObject树。PipelineOwnerBuildOwner都位于framework层,它们通过Bingding(胶水层)与engine交互。

  • 初始化了一个PipelineOwner用于管理RenderObject.
  • _handlePersistentFrameCallback这个callback传入SchedulerBinding中的_postFrameCallbacks中,这样在硬件每次发出VSync信号的时候都会调用RenderBinding中的_handlePersistentFrameCallback方法._handlePersistentFrameCallback方法中直接调用了drawFrame方法。

scheduleAttachRootWidget

实例化之后会调用scheduleAttachRootWidget方法。

1
2
3
4
5
6
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}

调用了attachRootWidget方法:

1
2
3
4
5
6
7
8
9
10
11
12
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance!.ensureVisualUpdate();
}
}

attachRootWidget方法用于是为根Widget生成一个根Element。生成Element调用了attachToRenderTree方法并传入了BuildOwner和Element。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
///新创建一个 element
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
// 建立能更新widget 树的能力,可以回调 callback,构建所有标记为dirty的elment
owner.buildScope(element!, () {
element!.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}

这个方法主要是element为空的时候新建一个element,新建后会调用BuildOwnerbuildScope主要作用是建立能更新widget 树的能力,可以回调 callback,构建所有标记为dirtyelement

attachRootWidget方法,最后会执行SchedulerBinding.instance!.ensureVisualUpdate()

1
2
3
4
5
6
7
8
9
10
11
12
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}

它主要是调用新的帧的调度管理。它会调用scheduleFrame方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled)
return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
///给window设置回调
ensureFrameCallbacksRegistered();
///调度更新
window.scheduleFrame();
_hasScheduledFrame = true;
}

这里会给window设置onBeginFrameonDrawFrame的回调,window会把回调传给platformDispatcher

1
2
3
4
5
6
7
8
9
10
@override
set onBeginFrame(ui.FrameCallback? callback) {
platformDispatcher.onBeginFrame = callback;
}
@override
ui.VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame;
@override
set onDrawFrame(ui.VoidCallback? callback) {
platformDispatcher.onDrawFrame = callback;
}

也就是说scheduleAttachRootWidget经过一系列调用之后,会把SchedulerBinding_handleBeginFrame_handleDrawFrame传给platformDispatcherplatformDispatcher分发来自enginee的事件。而在这里SingletonFlutterWindowplatformDispatcheronBeginFrameonDrawFrame这两个事件交给SchedulerBinding处理。

当硬件发出VSync信号时,会调用platformDispatcher的onDrawFrame。实际上会调用SchedulerBinding中的_handleDrawFrame方法。_handleDrawFrame会调用handleDrawFrame方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
} finally {
///省略代码
}
}

_postFrameCallbacks里面存储的是callback,作用是硬件每次发出VSync信号的时候都会调用。这里的_postFrameCallbacks是在RenderBinding这个mixininitInstances方法中传入的

1
addPersistentFrameCallback(_handlePersistentFrameCallback);

scheduleWarmUpFrame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);
});
Timer.run(() {
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();
_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;
Timeline.finishSync();
});
}

这个方法主要调用是scheduleFrame,跟进代码实际是调用的window.scheduleFrame(),

1
2
3
4
@override
void scheduleFrame() {
platformDispatcher.scheduleFrame();
}

window.scheduleFrame()调用了platformDispatcher.scheduleFrame(),通知engine层需要绘制。engine会根据情况尽快地调用platformDispatcher的onDrawFrame方法。

总结

runApp方法主要做了以下事情:

  • 创建WidgetsFlutterBinding它是连接frameworkengine的胶水层。注册Vsync回调,后面每一帧的调用会出发WidgetsFlutterBinding的回调,最后传递到framework层处理逻辑。
  • attachRootWidget:遍历挂载整个视图树,建立widgetelementrenderObjcect的连接关系。
  • scheduleWarmUpFrame:调度帧预热(warmUp)。执行帧绘制handleBeginFramehandleDrawFrame方法。