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<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}

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

注意此时的renderViewElement 肯定是null。

 

RenderObjectToWidgetAdapter.attachToRenderTree

RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> 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。

 

Element.inflateWidget

Element inflateWidget(Widget newWidget, dynamic newSlot) {
  assert(newWidget != null);
  final Key key = newWidget.key;
  if (key is GlobalKey) {
    final Element newChild = _retakeInactiveElement(key, newWidget);
    if (newChild != null) {
      assert(newChild._parent == null);
      newChild._activateWithParent(this, newSlot);
      final Element updatedChild = updateChild(newChild, newWidget, newSlot);
      assert(newChild == updatedChild);
      return updatedChild;
    }
  }

  final Element newChild = newWidget.createElement();
  newChild.mount(this, newSlot);
  return newChild;
}

有两步,

1. 如果是GlobalKey的话,会先从GlobalKey中获取引用的Element,如果有 有效的element的话就复用,

2. 如果 不是GlobalKey 或 没有从GlobalKey中获取到element 的话,就用widget调用其createElement()来创建了一个element,接着就把新建的子element挂载到了element树上。

 

element.mount主要有两个流程,

  • 一个是ComponentElement的,
  • 一个是RenderObjectElement的,

 

ComponentElement.mount

void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  assert(_child == null);
  assert(_active);
  _firstBuild();
  assert(_child != null);
}

super.mount会把parent记录在此element中。

firstBuild会调用到performRebuild方法:

void performRebuild() {
  Widget built;
  try {
    built = build();
  } catch (e, stack) {
  } finally {
  }
  try {
    _child = updateChild(_child, built, slot);
    assert(_child != null);
  } catch (e, stack) {
  }
}

此方法会首先调用build方法,来创建其子widget,ComponentElement主要有两类stateful和stateless,会有不同的实现。

然后就又调用到了updateChild方法,这就回到了上边流程,所以其实就是一直往下遍历创建widget树。

 

RenderObjectElement.mount

void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);

  _renderObject = widget.createRenderObject(this);

  attachRenderObject(newSlot);
  _dirty = false;
}

此方法会创建一个RenderObject,

之后就会把此RenderObject添加到RenderObject树上。

 

attachRenderObject

void attachRenderObject(dynamic newSlot) {
  assert(_ancestorRenderObjectElement == null);
  _slot = newSlot;
  _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
  _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);

  final ParentDataElement<ParentData> parentDataElement = _findAncestorParentDataElement();
  if (parentDataElement != null)
    _updateParentData(parentDataElement.widget);
}

1. _findAncestorRenderObjectElement往上找离它最近的父RenderObjectElement,这个和_parent可能不一样;然后把当前的renderObject给插入到父RenderObjectElement中。

2. 接着_findAncestorParentDataElement(),它的作用是找到离它最近的父ParentDataElement,并用ParentDataWidget上的数据更新此renderObject上的ParentData数据(具体作用可以看flutter2_widget_6_其他.docx 中的ParentDataWidget)。

 

状态更新时

标记element dirty

一般状态更新是StatefulWidget才可以的,所以这里分析StatefulWidget。

我们通过调用State.setState来标识当前需要更新子widget。

void setState(VoidCallback fn) {
  final dynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}

可以看到会先执行传递进来的函数类型参数,然后调用markNeedsBuild来标记此element需要更新其子widget。

 

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

  if (dirty)
    return;
  _dirty = true;
  owner.scheduleBuildFor(this);
}

此方法首先会判断是否已经调用过了,_dirty会在之后的rebuild后重新设置为false。

owner是BuildOwner,用来管理widget框架。此类用于追踪需要rebuilding的widget,并作为一个整体处理应用于widget树的其他任务,比如管理树的 inactive element列表,以及在调试时hot reload期间在必要时触发“reassemble”命令。

一般BuildOwner是由WidgetsBinding持有,并且与 build/layout/paint管道的其余部分一起被操作系统驱动。额外的BuildOwner可以被构建来管理off-screen widget trees。

要把一个BuildOwner分配给一个Element树,请使用RootRenderObjectElement.assignOwner 方法。

 

BuildOwner.scheduleBuildFor

void scheduleBuildFor(Element element) {
  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
    _scheduledFlushDirtyElements = true;
    onBuildScheduled();
  }
  _dirtyElements.add(element);
  element._inDirtyList = true;
}

首先调用onBuildScheduled()方法,此方法是一个回调,在WidgetsBinding中赋值的,

然后把此element加入到了_dirtyElements列表中了。

 

WidgetsBinding.initInstances

void initInstances() {
  super.initInstances();
  _instance = this;

  _buildOwner = BuildOwner();
  buildOwner.onBuildScheduled = _handleBuildScheduled;
}

 

回到上边onBuildScheduled()的调用,会调用到ensureVisualUpdate()

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;
  }
}

void scheduleFrame() {
  if (_hasScheduledFrame || !framesEnabled)
    return;

  ensureFrameCallbacksRegistered();
  window.scheduleFrame();
  _hasScheduledFrame = true;
}

最后会去调用底层window.scheduleFrame()来注册一个下一帧时回调,就类似于Android中的ViewRootImpl.scheduleTraversals()。

 

重建dirty Element的子widget

下一帧来到后,会调用到的方法是在上边的ensureFrameCallbacksRegistered()中注册的回调,

SchedulerBinding.ensureFrameCallbacksRegistered

void ensureFrameCallbacksRegistered() {
  window.onBeginFrame ??= _handleBeginFrame;
  window.onDrawFrame ??= _handleDrawFrame;
}

onBeginFrame :主要是用来执行动画,

onDrawFrame :这个主要处理上边说的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<FrameCallback> localPostFrameCallbacks =
        List<FrameCallback>.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.updateChild

Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  if (newWidget == null) {
    if (child != null)
      deactivateChild(child);
    return null;
  }
  Element newChild;
  if (child != null) {
    bool hasSameSuperclass = true;
    assert(() {
      final int oldElementClass = Element._debugConcreteSubtype(child);
      final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
      hasSameSuperclass = oldElementClass == newWidgetClass;
      return true;
    }());
    if (hasSameSuperclass && child.widget == newWidget) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      newChild = child;

    } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      child.update(newWidget);

      newChild = child;
    } else {
      deactivateChild(child);
      newChild = inflateWidget(newWidget, newSlot);
    }
  } else {
    newChild = inflateWidget(newWidget, newSlot);
  }

  return newChild;
}

由于不是第一次调用,所以Element child不是null,进入if逻辑,会进行三个判断:

  • 如果子widget不变,子element和子widget匹配(就是是否都是stateless,或都是stateful),那么更新slot。
  • 如果子element和子widget匹配,但子widget发生了变化,就调用子element.update(newWidget)来更新widget配置。
  • 最后一个判断是子element和子widget不匹配,那么就把老的child element加入到一个_inactiveElements列表中,然后进行重建element。

 

element.update

@mustCallSuper
void update(covariant Widget newWidget) {
  _widget = newWidget;
}

会把newWidget记录下来,

 

ComponentElement.update

  • StatelessElement.update
@override
void update(StatelessWidget newWidget) {
  super.update(newWidget);
  assert(widget == newWidget);
  _dirty = true;
  rebuild();
}

最后又调用rebuild,rebuild中会调用performRebuild()去重建其子widget。

 

  • StatefulElement.update

@override

@override
void update(StatefulWidget newWidget) {
  super.update(newWidget);
  assert(widget == newWidget);
  final StatefulWidget oldWidget = _state._widget;
  // Notice that we mark ourselves as dirty before calling didUpdateWidget to
  // let authors call setState from within didUpdateWidget without triggering
  // asserts.
  _dirty = true;
  _state._widget = widget as StatefulWidget;
  try {
    final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
  } finally {
  }
  rebuild();
}

回调state.didUpdateWidget,

最后又调用rebuild,rebuild中会调用performRebuild()去重建其子widget。

 

RenderObjectElement.update

RenderObjectElement.update

@override
void update(covariant RenderObjectWidget newWidget) {
  super.update(newWidget);
  assert(widget == newWidget);
  widget.updateRenderObject(this, renderObject);
  _dirty = false;
}

 

可以看下RenderObjectElement的几个重要的子类:

  • SingleChildRenderObjectElement.update
void update(SingleChildRenderObjectWidget newWidget) {
  super.update(newWidget);
  assert(widget == newWidget);
  _child = updateChild(_child, widget.child, null);
}

 

  • MultiChildRenderObjectElement.update
void update(MultiChildRenderObjectWidget newWidget) {
  super.update(newWidget);
  assert(widget == newWidget);
  _children = updateChildren(_children, widget.children, forgottenChildren: _forgottenChildren);
  _forgottenChildren.clear();
}

 

unmount流程

在上边重建流程中,如果子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;
}

 

posted @ 2021-01-07 11:46  嘤嘤嘤123  阅读(514)  评论(0编辑  收藏  举报