flutter_5_深入_1_深入widget树和构建流程


Build流程

BuildOwner.buildScope() 会有两种调用时机:

  • 树构建(应用启动时):runApp() 方法调用的 scheduleAttachRootWidget() 方法,它会构建Widgets Tree、Element Tree与RenderObject Tree三棵树。
  • 树更新(帧绘制与更新时):这里不会重新构建三棵树,而是只会更新dirty区域的Element。

也即是说树的构建和更新都是由 BuildOwner.buildScope() 方法来完成的。它们的差别在于树构建的时候传入了一个 element.mount(null, null) 回调。在 buildScope() 过程中会触发这个回调。

这个回调会构建三棵树,为什么会有三棵树呢,因为Widget只是对UI元素的一个抽象描述,我们需要先将其inflate成Element,然后生成对应的RenderObject来驱动渲染,如下所示:

  • Widget Tree:为Element描述需要的配置,调用createElement方法创建Element,决定Element是否需要更新。Flutter通过查分算法比对Widget树前后的变化,来决定Element的State是否改变。
  • Element Tree:表示Widget Tree特定位置的一个实例,调用createRenderObject创建RenderObject,同时持有Widget和RenderObject,负责管理Widget的配置和RenderObjec的渲染。Element的状态由Flutter维护,开发人员只需要维护Widget即可。
  • RenderObject Tree:RenderObject绘制,测量和绘制节点,布局子节点,处理输入事件。

首次

整个流程挺简单的,就从runApp方法一直往下跟就可以了。

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

WidgetsBinding.attachRootWidget

void attachRootWidget(Widget rootWidget) {
  _readyToProduceFrames = true;
  _renderViewElement = RenderObjectToWidgetAdapter(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement);
}

RenderObjectToWidgetAdapter继承自RenderObjectWidget,从RenderObject到Element树的桥梁。由runApp用于引导应用程序。

注意此时的renderViewElement 肯定是null。

RenderObjectToWidgetAdapter.attachToRenderTree

RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement element ]) {
  if (element == null) {
    owner.lockState(() {
      element = createElement();
      assert(element != null);
      element.assignOwner(owner);
    });
    owner.buildScope(element, () {
      element.mount(null, null);
    });
    // This is most likely the first time the framework is ready to produce
    // a frame. Ensure that we are asked for one.
    SchedulerBinding.instance.ensureVisualUpdate();
  } else {
    element._newWidget = this;
    element.markNeedsBuild();
  }
  return element;
}

createElement创建的element是RenderObjectToWidgetElement,他是一个RootRenderObjectElement,也就是根element。

element.mount(null, null);会向下遍历并构建整个widget树。

RenderObjectToWidgetElement.mount

@override
void mount(Element parent, dynamic newSlot) {
  assert(parent == null);
  super.mount(parent, newSlot);
  _rebuild();
}

RenderObjectToWidgetElement._rebuild

void _rebuild() {
  try {
    _child = updateChild(_child, widget.child, _rootChildSlot);
  } catch (exception, stack) {
  }
}

此时的widget就是RenderObjectToWidgetAdapter,它的widget.child就是runApp传递进去的widget。

Element.updateChild

Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  。。。
  Element newChild;
  if (child != null) {
    。。。
  } else {
    newChild = inflateWidget(newWidget, newSlot);
  }


  return newChild;
}

由于是第一次,Element child是null,执行else里的逻辑,inflateWidget使用子widget来创建一个子Element。

此时的newWidget是runApp传递进去的widget。

persistentCallbacks

void handleDrawFrame() {
  try {
    // PERSISTENT FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.persistentCallbacks;
    for (final FrameCallback callback in _persistentCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp!);

    // POST-FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.postFrameCallbacks;
    final List localPostFrameCallbacks =
        List.from(_postFrameCallbacks);
    _postFrameCallbacks.clear();
    for (final FrameCallback callback in localPostFrameCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp!);
  } finally {
  }
}

看下persistentCallbacks列表在哪添加的callback

最终找到是在RendererBinding.initInstances中添加的callback,

void initInstances() {
  super.initInstances();
  _instance = this;
  _pipelineOwner = PipelineOwner(
    onNeedVisualUpdate: ensureVisualUpdate,
    onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
    onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
  );

  initRenderView();

  addPersistentFrameCallback(_handlePersistentFrameCallback);
  initMouseTracker();
}

WidgetsFlutterBinding继承了RendererBinding。

接着上边handleDrawFrame的流程:

void _handlePersistentFrameCallback(Duration timeStamp) {
  drawFrame();
  _scheduleMouseTrackerUpdate();
}

WidgetBinding重载了drawFrame,把build流程加入进来了。

@override
void drawFrame() {

  try {
    if (renderViewElement != null)
      buildOwner.buildScope(renderViewElement);
    super.drawFrame();
    buildOwner.finalizeTree();
  } finally {
  }
}

buildOwner.finalizeTree();

buildOwner.buildScope

void buildScope(Element context, [ VoidCallback callback ]) {
  if (callback == null && _dirtyElements.isEmpty)
    return;
  try {
    _scheduledFlushDirtyElements = true;
    if (callback != null) {
      assert(_debugStateLocked);
      Element debugPreviousBuildTarget;
      _dirtyElementsNeedsResorting = false;
      try {
// 可以添加一个回调在build之前执行。
        callback();
      } finally {
      }
    }
    _dirtyElements.sort(Element._sort);
    _dirtyElementsNeedsResorting = false;
    int dirtyCount = _dirtyElements.length;
    int index = 0;
    while (index < dirtyCount) {
      try {
        _dirtyElements[index].rebuild();
      } catch (e, stack) {
      }
      index += 1;
      if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
        _dirtyElements.sort(Element._sort);
        _dirtyElementsNeedsResorting = false;
        dirtyCount = _dirtyElements.length;
        while (index > 0 && _dirtyElements[index - 1].dirty) {
          index -= 1;
        }
      }
    }
  } finally {
    for (final Element element in _dirtyElements) {
      element._inDirtyList = false;
    }
    _dirtyElements.clear();
    _scheduledFlushDirtyElements = false;
    _dirtyElementsNeedsResorting = null;
  }
}

可以看到会遍历dirtyElements列表中的element.rebuild()。

而element.rebuild()最终会调用到performRebuild()。

Element.performRebuild

接着会根据不同类型的element去 重建子widget 或 重建子element。

ComponentElement.performRebuild

void performRebuild() {
  Widget built;
  try {
    built = build();
  } catch (e, stack) {

  } finally {
    // We delay marking the element as clean until after calling build() so
    // that attempts to markNeedsBuild() during build() will be ignored.
    _dirty = false;
  }
  try {
    _child = updateChild(_child, built, slot);
  } catch (e, stack) {
  }
}

会先调用build创建一个子widget,然后调用Element.updateChild来更新。

RenderObjectElement.performRebuild

void performRebuild() {
  widget.updateRenderObject(this, renderObject);
  _dirty = false;
}

可以看到RenderObjectElement只是更新widget的配置。

element.update(newWidget)来更新widget配置。
  • 最后一个判断是子element和子widget不匹配,那么就把老的child element加入到一个_inactiveElements列表中,然后进行重建element。
  • 在上边重建流程中,如果子element和子widget不匹配,那么就把老的child element加入到一个_inactiveElements列表中。

    为什么要先加入到这里边,而不是直接unmount呢?

    这么做的原因一般是element的parent发生了改变,需要先从原来的树上移出(也就是加入到_inactiveElements中),然后在此帧后续的操作中如果真正需要复用时从列表中再取出,如果后续不需要那么就会在整个构建流程执行完时,框架会把这个列表中的element都给unmount了。

    经过搜索发现只有GlobalKey使用了这一逻辑。

    具体逻辑在,BuildOwner.finalizeTree()

    void finalizeTree() {
      Timeline.startSync('Finalize tree', arguments: timelineArgumentsIndicatingLandmarkEvent);
      try {
        lockState(() {
          _inactiveElements._unmountAll(); // this unregisters the GlobalKeys
        });
      } catch (e, stack) {
        // Catching the exception directly to avoid activating the ErrorWidget.
        // Since the tree is in a broken state, adding the ErrorWidget would
        // cause more exceptions.
        _debugReportException(ErrorSummary('while finalizing the widget tree'), e, stack);
      } finally {
        Timeline.finishSync();
      }
    }

    这个逻辑就是每帧的处理流程说的The finalization phase。

    对于StatefulElement的unmount,里边会调用state.dispose():

    void unmount() {
      super.unmount();
      _state.dispose();
      _state._element = null;
      _state = null;
    }