背景
在之前的文章【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
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/355364.html
標籤:其他
