1. 程式人生 > IOS開發 >深入淺出 Flutter Framework 之 BuildOwner

深入淺出 Flutter Framework 之 BuildOwner

本文是『 深入淺出 Flutter Framework 』系列文章的第二篇,對 BuildOwer 相關內容進行簡要地分析介紹,為下一篇文章介紹 Element 作準備 (由於篇幅原因將其單獨提出來)。

本文同時發表於我的個人部落格

本系列文章將深入 Flutter Framework 內部逐步去分析其核心概念和流程,主要包括:

Overview


BuildOwer在 Element 狀態管理上起到重要作用:

  • 在 UI 更新過程中跟蹤、管理需要 rebuild 的 Element (「dirty elements」);
  • 在有「dirty elements」時,及時通知引擎,以便在下一幀安排上對「dirty elements」的 rebuild,從而去重新整理 UI;
  • 管理處於 "inactive" 狀態的 Element。

這是我們遇到的第一個 Owner,後面還有PipeOwner

整棵「Element Tree」共享同一個BuildOwer例項 (全域性的),在 Element 掛載過程中由 parent 傳遞給 child element。

@mustCallSuper
void mount(Element parent,dynamic newSlot) {
  _parent = parent;
  _slot = newSlot;
  _depth = _parent != null
? _parent.depth + 1 : 1; _active = true; if (parent != null) // Only assign ownership if the parent is non-null _owner = parent.owner; } 複製程式碼

以上是Element基類的mount方法,第 8 行將 parent.owner 賦給了 child。

BuildOwer例項由WidgetsBinding負責建立,並賦值給「Element Tree」的根節點RenderObjectToWidgetElement,此後隨著「Element Tree」的建立逐級傳遞給子節點。(具體流程後續文章會詳細分析) 一般情況下並不需要我們手動例項化BuildOwer,除非需要離屏沉浸 (此時需要構建 off-screen element tree)

BuildOwer兩個關鍵成員變數:

final _InactiveElements _inactiveElements = _InactiveElements();
final List<Element> _dirtyElements = <Element>[];
複製程式碼

其命名已清晰表達了他們的用途:分別用於儲存收集到的「Inactive Elements」、「Dirty Elements」。

Dirty Elements


那麼BuildOwer是如何收集「Dirty Elements」的呢? 對於需要更新的 element,首先會呼叫Element.markNeedsBuild方法,如前文講到的State.setState方法:

void setState(VoidCallback fn) {
  final dynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}
複製程式碼

如下,Element.markNeedsBuild呼叫了BuildOwer.scheduleBuildFor方法:

void markNeedsBuild() {
  if (!_active)
    return;

  if (dirty)
    return;

  _dirty = true;
  owner.scheduleBuildFor(this);
}
複製程式碼

BuildOwer.scheduleBuildFor方法做了 2 件事:

  • 呼叫onBuildScheduled,該方法(其實是個callback)會通知 Engine 在下一幀需要做更新操作;
  • 將「Dirty Elements」加入到_dirtyElements中。
void scheduleBuildFor(Element element) {
  assert(element.owner == this);
  onBuildScheduled();

  _dirtyElements.add(element);
  element._inDirtyList = true;
}
複製程式碼

此後,在新一幀繪製到來時,WidgetsBinding.drawFrame會呼叫BuildOwer.buildScope方法:

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();
  }
}
複製程式碼
  • 如有回撥,先執行回撥 (第 7 行);
  • 對「dirty elements」按在「Element Tree」上的深度排序 (即 parent 排在 child 前面) (第 10 行);

為啥要這樣排?確保 parent 先於 child 被 rebuild,以免 child 被重複 rebuild (因為 parent 在 rebuild 時會遞迴地 update child)。

  • _dirtyElements中的元素依次呼叫rebuild (第 14 行);
  • 清理_dirtyElements (第 21 行)。

Inactive Elements


所謂「Inactive Element」,是指 element 從「Element Tree」上被移除到 dispose 或被重新插入「Element Tree」間的一箇中間狀態。 設計 inactive 狀態的主要目的是實現『帶有「global key」的 element』可以帶著『狀態』在樹上任意移動。

BuildOwer 負責對「Inactive Element」進行管理,包括新增、刪除以及對過期的「Inactive Element」執行 unmount 操作。 關於「Inactive Element」的更多資訊將在介紹 Element 時一起介紹。

小結

BuildOwner 主要是用於收集那些需要 rebuild 的「Dirty Elements」以及處於 Inactive 狀態的 Elements。

結束了!就是這麼簡單,下篇再見!