Flutter启动流程初探

最近开始研究Flutter了,俗话说工欲善其事必先利其器,在正式运用Flutter之前肯定要先了解了解它的工作机制,于是开始了Flutter以及Dart的源码学习之旅,这次就简单的分析一下Flutter的启动流程,作为记录~

hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

上面是官网上一个demo的一部分,我们可以看到其中有一个main函数,内部使用了runApp并将业务视图顶层的MyApp传了进去。这样我们的Flutter界面就能展示出来了。

runApp

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

既然如此,我们就来看runApp的源码吧,内部逻辑很简单,就是通过WidgetsFlutterBinding去初始化一些逻辑(ensureInitialized)方法,然后调用attachRootWidget并将我们的MyApp组件传入。

attachRootWidget

1
2
3
4
5
6
7
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}

attachRootWidget方法中,通过RenderObjectToWidgetAdapter的attachToRenderTree去创建顶层视图的element(renderViewElement)。

attachToRenderTree

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}

这个方法就很有讲究了,首先如果element是空,则调用createElement方法去创建,然后通过mount方法将其挂载到视图树上。

createElement

1
2
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

createElement方法就是创建了一个RenderObjectToWidgetElement。由此我们知道,Flutter顶层视图的element就是这个RenderObjectToWidgetElement。然后我们再来看它的mount方法。

mount

1
2
3
4
5
6
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void _rebuild() {
try {
_child = updateChild(_child, widget.child, _rootChildSlot);
assert(_child != null);
} catch (exception, stack) {
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: 'attaching to the render tree'
);
FlutterError.reportError(details);
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
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
35
36
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
assert(() {
if (newWidget != null && newWidget.key is GlobalKey) {
final GlobalKey key = newWidget.key;
key._debugReserveFor(this);
}
return true;
}());
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner._debugElementWasRebuilt(child);
return true;
}());
return child;
}
deactivateChild(child);
assert(child._parent == null);
}
return inflateWidget(newWidget, newSlot);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
assert(newWidget != null);
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() { _debugCheckForCycles(newChild); return true; }());
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
return updatedChild;
}
}
final Element newChild = newWidget.createElement();
assert(() { _debugCheckForCycles(newChild); return true; }());
newChild.mount(this, newSlot);
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}

可以看到mount方法最终调用了inflateWidget方法。inflateWidget方法中,通过newWidget的createElement方法创建了子element并调用其mount方法。

这个newWidget是什么呢?

1
_child = updateChild(_child, widget.child, _rootChildSlot);

这个是前面讲到的顶层element RenderObjectToWidgetElement的_rebuild,可以看到newWidget相对应传入的是widget.child,那么这个widget又是什么呢?我们继续往前推,看一下RenderObjectToWidgetElement是如果构造的。

1
RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);

这个是它的构造函数,可以看到上文提到的widget就是通过构造函数传进来的。

1
2
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

这个是RenderObjectToWidgetAdapter构造RenderObjectToWidgetElement的方法。有次我们得知,上文提到的widget就是RenderObjectToWidgetAdapter。那么updateChild中的widget.child就是RenderObjectToWidgetAdapter的child变量。

回到最开始的attachRootWidget方法:

1
2
3
4
5
6
7
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}

非常清楚了,RenderObjectToWidgetAdapter的child变量就是我们runApp中传进来的rootWidget,也就是例子中的MyApp。

总结一下

下面我们来总结一下上面的流程:

  1. Flutter通过runApp方法启动整个应用。
  2. runApp中通过attachRootWidget方法创建顶层视图的element,而这个element是RenderObjectToWidgetElement。
  3. 创建完毕之后调用element的mount方法挂载。
  4. mount方法最后,会调用runApp中传进来的业务rootWidget的createElement方法创建element。
  5. 最后调用element的mount方法。

遍历树

上文最后我们得知,根视图element的mount方法最终会调用业务根视图的element的mount方法,那么我们带入我们的demo,业务根视图MyApp是继承自StatelessWidget。

1
2
3
4
abstract class StatelessWidget extends Widget {
@override
StatelessElement createElement() => StatelessElement(this);
}

它的createElement方法创建了一个StatelessElement。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StatelessElement extends ComponentElement {
/// Creates an element that uses the given widget as its configuration.
StatelessElement(StatelessWidget widget) : super(widget);

@override
StatelessWidget get widget => super.widget;

@override
Widget build() => widget.build(this);

@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
}

StatelessElement继承自ComponentElement,我们来看一下ComponentElement的mount方法。

1
2
3
4
5
6
7
8
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_active);
_firstBuild();
assert(_child != null);
}
1
2
3
void _firstBuild() {
rebuild();
}
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 rebuild() {
assert(_debugLifecycleState != _ElementLifecycle.initial);
if (!_active || !_dirty)
return;
assert(() {
if (debugOnRebuildDirtyWidget != null) {
debugOnRebuildDirtyWidget(this, _debugBuiltOnce);
}
if (debugPrintRebuildDirtyWidgets) {
if (!_debugBuiltOnce) {
debugPrint('Building $this');
_debugBuiltOnce = true;
} else {
debugPrint('Rebuilding $this');
}
}
return true;
}());
assert(_debugLifecycleState == _ElementLifecycle.active);
assert(owner._debugStateLocked);
Element debugPreviousBuildTarget;
assert(() {
debugPreviousBuildTarget = owner._debugCurrentBuildTarget;
owner._debugCurrentBuildTarget = this;
return true;
}());
performRebuild();
assert(() {
assert(owner._debugCurrentBuildTarget == this);
owner._debugCurrentBuildTarget = debugPreviousBuildTarget;
return true;
}());
assert(!_dirty);
}
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
35
@override
void performRebuild() {
assert(() {
if (debugProfileBuildsEnabled)
Timeline.startSync('${widget.runtimeType}', arguments: timelineWhitelistArguments);
return true;
}());

assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
Widget built;
try {
built = build();
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
_child = updateChild(null, built, slot);
}

assert(() {
if (debugProfileBuildsEnabled)
Timeline.finishSync();
return true;
}());
}

最终调用了performRebuild方法,而在这个方法中:

1
_child = updateChild(_child, built, slot);

又回去调用前文提到的updateChild方法,这样就做到了遍历整个视图树,创建视图了。

最后

最后,runApp中我们还有一个方法没有提到:

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

那就是scheduleWarmUpFrame,该方法在attachRootWidget之后,遍历挂载完了整个视图树,通过scheduleWarmUpFrame方法去渲染,具体逻辑之后有机会再深究吧~