BuildOwner是负责管理widgets框架的类。
classDiagram
class BuildOwner {
onBuildScheduled: VoidCallback
_inactiveElements: _InactiveElements
_dirtyElements: List<Element>
scheduleBuildFor() void
}
BuildOwner 一开始是由 WidgetsBinding 在initInstances 里构造。
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;
platformDispatcher.onLocaleChanged = handleLocaleChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.backGesture.setMethodCallHandler(
_handleBackGestureInvocation,
);
assert(() {
FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
return true;
}());
platformMenuDelegate = DefaultPlatformMenuDelegate();
}
....
}
同时会设置BuildOwner的onBuildScheduled 的参数值,而这个onBuildScheduled值是_handleBuildScheduled 。他最终调用platformDispatcher.scheduleFrame()向native层申请调度下一帧的渲染。
void _handleBuildScheduled() {
ensureVisualUpdate();
}
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
在首帧渲染的时候,会先调用根widget-RootWidget的attach方法来创建RootWidget 的Element对象,然后调用assignOwner 方法来完成跟BuildOwner的关联。然后在构建Element树的时候会调用每个子Widget的mount方法来构建Element树。mount的方法会将父的Element的BuildOwner传递给子Element进行关联。这样就给整个Element树都赋予了重绘的能力。
StatefulWidget的State调用setState的时候会调用_element!.markNeedsBuild(); 然后 调用BuildOwner的scheduleBuildFor(this);
该方法会将当前Element的dirty 状态置为true,表示当前元素是脏元素需要更新。
然后调用onBuildScheduled 来向native申请调度下一帧渲染,然后将通过将当前这个Element对像添加进BuildOwner对象的_dirtyElements 里。之后native通知渲染的时候会调用WidgetsBinding的 drawFrame 方法完成构建。
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
**onBuildScheduled**!();
}
**_dirtyElements.add(element);**
element._inDirtyList = true;
}
drawFrame方法会调用buildScope 方法,buildScope方法会调用BuildScope方法。BuildScope方法会遍历每个脏元素调用它们的rebuild方法来更新。
这个行为执行完后,清除标记状态。这个过程发生的过程中很有可能部分Element会被卸载-unmount。卸载的Element会被标记为inactivate状态,然后添加进BuildOwner的_inactiveElements 中,这些Element 不会被立马让他成为可能被回收的对象,也就是不会马上调用clear。目的是为了能复用这些Element。
例如一个页面的一个按钮的一个位置挪到另一个widget里的位置中。那这个按钮的Element 会被卸载,但是这个Element 从Widget树看来只是从一个位置挪到为另一个Widget结点的子树。为了避免了这个元素被重复创建,则添加进_inactiveElements 里,在Element树更新,重绘的时候,由于Widget树结构发生变化,就会有大量的Widget需要进行加载,则会调用inflateWidget ,里面会调用_retakeInactiveElement 来优先尝试能不能从之前被卸载的元素列表来加载该Widget。可以的话就能直接复用了。
最终构建完后调用BuildOwner的finalizeTree 方法将_inactiveElements进行清理
sequenceDiagram
drawFrame->>+BuildOwner: buildOwner!.buildScope
BuildOwner->>+Element:
loop
Note right of BuildOwner: for each(elements){elements.build}
BuildOwner-->Element:
end
BuildOwner-->>-drawFrame:
drawFrame->>+BuildOwner: buildOwner!.finalizeTree
BuildOwner-->>-drawFrame: