Flutter|UI绘制解析
# Flutter UI绘制解析
# Widget、Element、RenderObject概念
因为WidgetTree会大量的重绘,所以Widget必然是廉价的。
Flutter UI有三大元素:Widget、Element、RenderObject。对应这三者也有三个owner负责管理他们,分别是WidgetOwner、BuildOwner、PipelineOwner。
- Widget 并不是真正的渲染对象,它只是一些廉价的纯对象,持有一些渲染需要的配置信息,它随时不断的被替换着。
- RenderObject 是真正渲染对象,渲染引擎会根据RenderObject来进行真正的绘制,它是相对稳定且昂贵的。
- Element 使得不断变化的Widget转变为相对稳定的RenderObject的中间者。
WidgetOwner在不断改变widget,然后向BuildOwner输送一个个WidgetTree,首次的WidgetTree会生成一个与之对应的ElementTree,并生成对应的RenderObjectTree。
BuildOwner每次收到新的WidgetTree就与上一次的进行对比,在ElementTree上只更新变化的部分,Element有可能仅是update一下,也有可能会被替换,Element被替换之后,与之对应的RenderObject也就被替换了。虽然WidgetTree全部被替换了,但ElementTree和RenderObjectTree只替换了变化的部分。
PipelineOwner管理着真正需要绘制的View。它会对RenderObjectTree中发生变化节点的进行flush操作,最后交给底层引擎渲染。
# Widget、Element、RenderObject联系
Widget赋予了所有的Widget子类三个关键的能力:保证自身唯一以及定位的Key、创建Element的createElement、是否能更新的canUpdate。
Widget子类有RenderObjectWidget,它代表着有渲染能力,拥有一个创建RenderObject的createRenderObject方法。
不难看出,Widget、Element、RenderObject的创建关系并不是线性传递的,Element和RenderObject都是Widget创建出来的,也并不是每一个Widget都有与之对应的RenderObjectWidget。这也解释图中RenderObjectTree看起来和前面的WidgetTree缺少了一些节点。
# Widget、Element、RenderObject 的第一次创建与关联
Flutter的WidgetTree很简单,只有最底层的RootWidget。
RootWidget
MyApp
(自定义)MyMaterialApp
(自定义)
void runApp(Widget app) {
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
...
binding
..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
..scheduleWarmUpFrame();
}
2
3
4
5
6
7
WidgetsFlutterBinding mixin 了一系列的Binding,这些Binding持有了上面说的一些owner,比如BuildOwner、PipelineOwner,所以随着WidgetsFlutterBinding的初始化,其他的Binding也被初始化了,此时Flutter启动!
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
attachToBuildOwner(RootWidget(
debugShortDescription: '[root]',
child: rootWidget,
));
}
void attachToBuildOwner(RootWidget widget) {
final bool isBootstrapFrame = rootElement == null;
_readyToProduceFrames = true;
_rootElement = widget.attach(buildOwner!, rootElement as RootElement?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
RootElement attach(BuildOwner owner, [ RootElement? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement(); // 创建rootElement
assert(element != null);
element!.assignOwner(owner); // 绑定BuildOwner
});
owner.buildScope(element!, () { //子widget的初始化从这里开始
element!.mount(/* parent */ null, /* slot */ null); // 初始化子Widget前,先执行rootElement的mount方法
});
} 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
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
binding.wrapWithDefaultView(app)
方法创建了Root[Widget]
scheduleAttachRootWidget
中的attachRootWidget
方法调用widget.attach
创建了Root[Element](RootElement)
- 使用
element!.assignOwner(owner)
,绑定了BuildOwner
Root[Element]
尝试调用mount方法将自己挂载到父Element上,因为自己就是root了,所以没有父Element,挂空- mount的过程中会调用
Widget.createRenderObject
创建了Root[RenderObject]
代码实现
class RootElement extends Element with RootElementMixin { ... void mount(Element? parent, Object? newSlot) { assert(parent == null); // We are the root! super.mount(parent, newSlot); _rebuild(); assert(_child != null); super.performRebuild(); // clears the "dirty" flag } ... } mixin RootElementMixin on Element { ... void mount(Element? parent, Object? newSlot) { // Root elements should never have parents. assert(parent == null); assert(newSlot == null); super.mount(parent, newSlot); } ... } abstract class RenderObjectElement extends Element { void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); ... _renderObject = (widget as RenderObjectWidget).createRenderObject(this); ... attachRenderObject(newSlot); super.performRebuild(); // clears the "dirty" flag } }
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它的child,也就是我们传入的app是怎么挂载父控件上的呢?
- 我们将app作为参数传给了
Root[Widget]
,app[Widget]
也就成了为Root[Widget]
的child[Widget]
- 调用
owner.buildScope
,开始执行子Tree的创建以及挂载!此时root节点会先调用创建root - 调用
createElement
方法创建出Child[Element]
- 调用
Element
的mount
方法,将自己挂载到Root[Element]
上,形成一棵树 - 挂载的同时,调用
widget.createRenderObject
,创建Child[RenderObject]
- 创建完成后,调用
attachRenderObject
,完成和Root[RenderObject]
的链接
就这样,WidgetTree、ElementTree、RenderObject创建完成,并有各自的链接关系。
# Flutter的刷新流程:Element的复用
虽然createRenderObject方法的实现是在Widget当中,但持有RenderObject引用的却是RenderObjectElement。
abstract class RenderObjectElement extends Element {
...
RenderObjectWidget get widget => super.widget;
RenderObject get renderObject => _renderObject;
RenderObject _renderObject;
}
2
3
4
5
6
7
8
9
10
Element同时持有两者。Root Widget,Root Element,Root RenderObject都已经创建完成并且三者链接成功。
# 刷新机制
通知底层渲染
setState
-> _element.markNeedsBuild()
-> owner.scheduleBuildFor(this)
-> _dirtyElements.add(element)
-> onBuildScheduled!()
-> _handleBuildScheduled
-> ensureVisualUpdate
-> scheduleFrame
-> ensureFrameCallbacksRegistered
- _handleDrawFrame
- handleDrawFrame
-> _invokeFrameCallback(callback, _currentFrameTimeStamp!)
->_persistentCallbacks
-> addPersistentFrameCallback
-> addPersistentFrameCallback(_handlePersistentFrameCallback);
-> drawFrame()
底层回调dart层
void buildScope(Element context, [VoidCallback callback]) {
...
try {
...
//1.按照Element的深度从小到大,对_dirtyElements进行排序
_dirtyElements.sort(Element._sort);
...
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
try {
//2.遍历rebuild
_dirtyElements[index].rebuild();
} catch (e, stack) {
}
index += 1;
}
} finally {
for (Element element in _dirtyElements) {
element._inDirtyList = false;
}
//3.清空
_dirtyElements.clear();
...
}
}
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