原文传送门:
调用图
我们用下面的例子来分析Flutter启动所做的事情
void main() {
runApp(MyApp());
}
<!-- more -->
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
Widget build(BuildContext context) {
return Container();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
初始化Binding
runApp
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
2
3
4
5
在runApp
中,我们会调用WidgetsFlutterBinding.ensureInitialized()
来初始化Binding
。
Binding
Binding
是一系列单例,在启动的时候初始化,其中包括了WidgetsFlutterBinding
、BindingBase
、GestureBinding
、SchedulerBinding
、ServicesBinding
等,下面来分析它们的作用
- BindingBase: 是所有类的基类,负责初始化其它类、初始化一些Native相关的信息(如平台是Android还是iOS)、注册基本的Native事件(如热更新、退出)
- GestureBinding: 提供
window.onPointerDataPacket
回调,接受Native事件,负责事件转换及分发 - SchedulerBinding: 使用了
window.scheduleFrame
来通知Native及使用window.onBeginFrame
和window.onDrawFrame
回调来接收消息,主要是负责通知Native在下一侦的事件下发与事件注册,当我们调用setState后,就会触发此类的方法,等待事件下发后进行渲染 - ServicesBinding: 使用
window.onPlatformMessage
回调,负责通道相关的初始化及通信相关的处理 - PaintingBinding: 与绘制相关的函数绑定,还处理一些图片渲染相关的缓存
- SemanticsBinding: 注册平台相关的辅助函数
- RendererBinding: 初始化
PipelineOwner
、renderView
、onMetricsChanged
、onTextScaleFactorChanged
、onPlatformBrightnessChanged
、onSemanticsEnabledChanged
onSemanticsAction
等,用于监听并处理平台渲染相关如字体、状态栏改变时的事件,是渲染树与Flutter engine沟通的桥梁 - WidgetsBinding: 初始化
BuildOwner
,注册window.onLocaleChanged
、onBuildScheduled
等回调。它是Flutter widget层与engine的桥梁。
渲染树的构建
attachRootWidget
[-> packages/flutter/lib/src/widgets/binding.dart]
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
}
2
3
4
5
6
7
8
上面会先初始化一个RenderObjectToWidgetAdapter
,它是一个特殊的Widget
,主要用Flutter树根节点的初始化,其中传了一个renderView
,上面提到了是在RendererBinding初始化时就已经初始化了,它是根RenderObject
节点,rootWidget
就是我们runApp时传入的Widget
。
后面调用了attachToRenderTree
会开始一系列的树构建
attachToRenderTree
[-> packages/flutter/lib/src/widgets/binding.dart]
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
// 如果element==null执行,第一次渲染element一定为null
if (element == null) {
owner.lockState(() {
// 调用createElement创建Element
element = createElement();
assert(element != null);
// 将owner传给element,让element持有owner
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
element!.mount(null, null);
});
SchedulerBinding.instance!.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
上面会先根据createElement
创建一个Element
对象,然后传入owner
,BuildOwner
是Flutter处理更新的类,主要是处理脏节点收集与重绘,需要注意的是,根据Flutter的设计,全局只有一个BuildOwner
对象,在此处传入后,它会在子节点传递。
然后会调用owner.buildScape
,buildScape
主要提供两个作用,一是为Widget Tree
更新建立一个作用域并执行回调(像上面这种情况,不过回调也可以不传),二是当作用域中存在脏节点,它会调用Element.rebuild
方法进行重新构建(就在此时调用的Widget的build重新渲染Element)。
接着会调用element.mount
,这是一个Element
非常重要的方法,这里就开始构建三棵树了.mount
最简单的作用就是为当前对象绑定父对象及为当前绑定_inheritedWidgets
(你认为的没错,就是Flutter中用于数据共享的InheritedWidget
的Element
)。一般我们的子Element
会重写mount
方法,用于孩子节点的初始化。
mount
[-> packages/flutter/lib/src/widgets/framework.dart]
void mount(Element? parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
void _rebuild() {
//...
_child = updateChild(_child, widget.child, _rootChildSlot);
//...
}
2
3
4
5
6
7
8
9
10
11
12
所以在RenderObjectToWidgetElement
中会调用_rebuild
然后调用updateChild
来初始化孩子节点。
updateChild
[-> packages/flutter/lib/src/widgets/framework.dart]
? updateChild(Element? child, Widget? newWidget, dynamic newSlot) {
// ...
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
//...
child.update(newWidget);
//...
newChild = child;
} else {
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
Element
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
updateChild
方法用于初始化或者更新孩子节点,当child != null
时,主要是调用child.update
来更新child
,否则调用inflateWidget
,这里第一次渲染当然是会调用inflateWidget
inflateWidget
[-> packages/flutter/lib/src/widgets/framework.dart]
inflateWidget(Widget newWidget, dynamic newSlot) {
// ...
final Element newChild = newWidget.createElement();
// ...
newChild.mount(this, newSlot);
return newChild;
}
Element
2
3
4
5
6
7
8
inflateWidget
方法的作用就是根据Widget
生成Element
同时调用child.mount
。这样,一次树的循环调用就接上了。
Widget树构建
有人问StatelessWidget
和StatefulWidget
的build方法怎么都没调用。StatelessWidget
、StatefulWidget
对应的Element
是StatelessElement
和StatefulElement
,它们都是继承至ComponentElement
。根据上面的分析,mount
方法是构建起点,我们可以看下ComponentElement
中的mount
方法。
_firstBuild
[-> packages/flutter/lib/src/widgets/framework.dart]
void mount(Element? parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_lifecycleState == _ElementLifecycle.active);
_firstBuild();
assert(_child != null);
}
2
3
4
5
6
7
8
调用了_firstBuild
方法,然后再调用了Element
的rebuild
方法,接着调用到ComponentElement
的performRebuild
方法
performRebuild
[-> packages/flutter/lib/src/widgets/framework.dart]
void rebuild() {
performRebuild();
}
//...
void performRebuild() {
Widget? built;
try {
// ...
built = build();
} catch (e, stack) {
// ...
} finally {
_dirty = false;
}
try {
_child = updateChild(_child, built, slot);
} catch (e, stack) {
// ...
_child = updateChild(null, built, slot);
}
if (!kReleaseMode && debugProfileBuildsEnabled)
Timeline.finishSync();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
performRebuild
是ComponentElement
实现的父类,其中调用了build
方法返回一个Widget
,然后就看到了熟悉的updateChild
方法,这就接上了上面的分析。
RenderObject树的构建
RenderObjectWidget
RenderObject
是Flutter中用于实际绘制的类,对于StatefulWidget
和StatelessWidget
实际上都是没有对应的RenderObject
节点的,实际上它们只是提供一个容器,用来组合各种绘制。
只有继承了RenderObjectWidget
的Widget
才有对应的RenderObject
节点。Flutter中为了方便,又提供了三种继承至RenderObjectWidget
的类,每个类都有不同作用
- LeafRenderObjectWidget 用于只有渲染功能,无子节点 (如Switch、Radio等)
- SingleChildRenderObjectWidget 只含有一个子节点 (如SizedBox、Padding等)
- MultiChildRenderObjectWidget 含有多个子节点(如Column、Stack等)
上面这些对象只是提供了一个容器给开发者使用,不过不是我们这次分析重点,先看看RenderObjectWidget
[-> packages/flutter/lib/src/widgets/framework.dart]
abstract class RenderObjectWidget extends Widget {
const RenderObjectWidget({ Key? key }) : super(key: key);
@factory
RenderObjectElement createElement();
@factory
RenderObject createRenderObject(BuildContext context);
void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
void didUnmountRenderObject(covariant RenderObject renderObject) { }
}
2
3
4
5
6
7
8
9
10
11
12
13
它提供了createRenderObject
方法来构建RenderObject
,看下其对应的Element
->RenderObjectElement
的mount
方法
createRenderObject
[-> packages/flutter/lib/src/widgets/framework.dart]
void mount(Element? parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
void attachRenderObject(dynamic newSlot) {
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
// 将上一步生成的renderObject插入到父renderObject中
_ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
其会先调用createRenderObject
来构建RenderObject
,然后调用attachRenderObject
将上一步生成的renderObject插入到父renderObject中。这里的通过_findAncestorParentDataElement
来查找的父类,因为上面说过,不是每个Element
都有对应的RenderObject
节点。
_findAncestorRenderObjectElement
[-> packages/flutter/lib/src/widgets/framework.dart]
RenderObjectElement? _findAncestorRenderObjectElement() {
Element? ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement)
ancestor = ancestor._parent;
return ancestor as RenderObjectElement?;
}
2
3
4
5
6
所以这里通过父Element
在Element树
中找上一个为RenderObjectElement
的类,而insertRenderObjectChild
是由上面说的SingleChildRenderObjectWidget
、MultiChildRenderObjectWidget
来具体实现的,主要是将生成的RenderObject
插入到刚刚查找的父RenderObject
中。因为根Element节点RenderObjectToWidgetElement
是继承至RenderObjectElement
,所以,只要通过RenderObjectToWidgetElement.renderObject
即可拿到整棵树RenderObject树。
总结
上面我们分析了Flutter第一次启动时的大致过程,WidgetsFlutterBinding
是总工头,几乎App所有的事情都是通过它来管理,BuildOwner
是构建树的负责人,它负责树的构建和更新,RenderObjectToWidgetAdapter
是一个特殊的Widget
,它是渲染树的根节点,也是构建树的起点,Widget
只是负责与开发人员对接,它就是一个工具人,开发人员把数据放到这里,后面都是通过Element
进行处理并生成RenderObject
(有些Widget不一定会生成RenderObject)。
现在我们只是生成了一个渲染树,可是界面到底是如何渲染的呢,回到上面的attachToRenderTree
方法,其中构件树生成后会调用SchedulerBinding.instance!.ensureVisualUpdate
方法,这个方法最后会调用window.scheduleFrame
,它会发送通知给Native:我这边树都生成好了,你在下一侦给我发个通知,我要开始渲染页面啦。
想看渲染过程,请听下回分解。