Flutter核心类分析深入理解BuildOwner
Posted 牧羊人.阿标
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter核心类分析深入理解BuildOwner相关的知识,希望对你有一定的参考价值。
背景
在之前的文章【Flutter原理】三棵树的诞生与核心流程一文中,我们第一次接触到了buildOwner,也就是在WidgetsBinding
对象的attachRootWidget(widget rootWidget)方法中,我们现在来回顾一下:
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
文章只是简单介绍了下BuildOwner可以理解为widgets管理器,至于它的具体来源与作用,就是我们本文分析的主要内容。
BuildOwner来源
接着上面的代码继续来看buildOwner是如何初始化的
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
// .......
@override
void initInstances() {
super.initInstances();
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
}
// ......
}
我们看到buildOwner的初始化是在mixin类WidgetsBinding的initInstances方法中初始化,它的调用者也就是在我们之前分析的FLutter APP启动的runApp()方法。
既然_buildOwner
是在WidgetsBinding中初始化的,那么_buildOwner
又是交给谁去使用呢?谁来管理,用来干啥?我们继续看最上面调用的attachToRenderTree
方法
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
//element 为空,说明是rootElement
owner.lockState(() {
element = createElement();
assert(element != null);
//步骤1 给rootElement分配Owner
element!.assignOwner(owner);
});
//步骤2 调用buildScope
owner.buildScope(element!, () {
//步骤3
element!.mount(null, null);
});
SchedulerBinding.instance!.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
- 步骤1:我们看到如果element == null的时候,就会就会创建rootElement调用rootEelement.assignOwner(owner),将owner交给element,
- 步骤2:调用buildScope方法,我们先继续看owner.buildScope方法
// 去掉一大堆的assert方法,保留核心逻辑
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
try {
if (callback != null) {
//先执行回调
callback();
}
_dirtyElements.sort(Element._sort);
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
_dirtyElements[index].rebuild();
index += 1;
}
} finally {
for (Element element in _dirtyElements) {
element._inDirtyList = false;
}
_dirtyElements.clear();
}
}
-
有回调,就先执行回调,也就是执行上面的
element!.mount(null, null);
方法。 -
对_dirtyElements进行排序,根据Element的depth进行排序,按照element tree上的深度
这样做的好处就是确保parent优先于child被rebuild,避免child被重复的rebuild(因为parent在rebuild的时候会递归的updateChild)
-
对_dirtyElement中的元素异常调用rebuild(
_dirtyElements[index].rebuild()
) -
清理_dirtyElement,将所有的 _dirtyElement设置为false。
我们再来看element!.mount
方法:
@override
void mount(Element? parent, dynamic newSlot) {
assert(parent == null);
// 步骤1
super.mount(parent, newSlot);
// 步骤2
_rebuild();
}
这里调用super.mount()也就是最终调用到了基类element.mount方法,我们稍后继续看
void mount(Element? parent, dynamic newSlot) {
// .... 一大堆的assert
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) // Only assign ownership if the parent is non-null
// 这里直接将parent.owner赋值给了child
_owner = parent.owner;
final Key? key = widget.key;
if (key is GlobalKey) {
key._register(this);
}
_updateInheritance();
}
好了到了这里我们来个阶段性的总结
BuildOwner
实例由WidgetsBinding
负责创建,并赋值给「Element Tree」的根节点RenderObjectToWidgetElement
,此后随着「Element Tree」的创建逐级传递给子节点。整棵「Element Tree」共享同一个BuildOwner
实例。
一般情况下并不需要我们手动实例化BuildOwner
,除非需要离屏沉浸 (此时需要构建 off-screen element tree)
BuildOwner主要作用
要分析BuildOwner
的作用,我们首先来看它的两个成员变量
//用于存储收集到的Inactive Elements
final _InactiveElements _inactiveElements = _InactiveElements();
//用于存储手机DirtyElement
final List<Element> _dirtyElements = <Element>[];
Dirty Elements
字面理解Dirty Elements就是脏的Element。那么就好理解了,也就是需要更新的Elements。
问题来了,BuildOwner是如何收集Dirty Elements的呢?我们需要跟到State.setState方法:
void setState(VoidCallback fn) {
final dynamic result = fn() as dynamic;
_element!.markNeedsBuild();
}
接下来看markNeedsBuild()
void markNeedsBuild() {
_dirty = true;
owner!.scheduleBuildFor(this);
}
markNeedsBuild
的主要作用就是设置_dirty属性为true,然后执行BuildOwner.scheduleBuildFor
方法
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled!();
}
_dirtyElements.add(element);
element._inDirtyList = true;
}
scheduleBuildFor
的逻辑也很清晰,只做了两件事:
- 调用onBuildScheduled,该方法实际上是一个回调(也就是文章开始提到的
buildOwner!.onBuildScheduled = _handleBuildScheduled;
这里这个方法),通知Engine在下一帧时做更新操作 - 将设置
_dirty
为true的element,加入到_dirtyElements
中。
在下一帧到来的时候,WidgetsBinding.drawFrame
会被调用
void drawFrame() {
try {
if (renderViewElement != null)
buildOwner!.buildScope(renderViewElement!);
super.drawFrame();
buildOwner!.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
}
在WidgetsBinding.drawFrame
方法内部,又调用到了buildOwner.buildScope
方法。buildScope作用就是rebuild所有的dirty elements再重新清理dirty elements。到了这里就形成了一个完整的闭环。
下面继续看InactiveElement
InactiveElement
所谓InactiveElement,按字面理解就是不活跃的Element,是指从Element Tree上被移除的element,我们来看下Element.deactivateChild
方法
void deactivateChild(Element child) {
child._parent = null;
child.detachRenderObject();
owner!._inactiveElements.add(child); // this eventually calls child.deactivate()
}
deactivateChild
的作用很明显,也就是将给定的Element移动到非活动的Element列表,在Element在detach的时候会被收集到_inactiveElements。
inactive elements在手动调用finalizeTree
的时候会被统一清理。
总结
BuildOwner的主要作用就是
- 在UI更新过程中跟踪,管理需要rebuild的Element(dirty elements)
- 在有dirty elements时,及时通知引擎,以便在下一帧安排dirty elements的rebuild,从而去更新UI
- 管理处于inactive状态的element
以上是关于Flutter核心类分析深入理解BuildOwner的主要内容,如果未能解决你的问题,请参考以下文章