Flutter的Rebuild流程源码分析

679 阅读6分钟

Flutter三棵树

Widgets树

对应Widget build()函数中构建的对象,整棵树每次build时都会被重新构建,由于是纯dart对象,所以创建起来很快速。

Widget由用户构建,声明式的描述组件布局、状态,相当于组件的配置。

Element树

每个Widget都有一个createElement()函数,用来创建对应的Element对象。Element树主要作为Widgets树和RenderObject树的中介,因为Widget会频繁的进行重建,Element树用来维持属性,保持组件在树种的位置等信息,同时持有Widget和RenderObject双方引用。

Widget和Element是一对一的,但Element和RenderObject并不是,只有RenderObjectElement才会持有RednerObject的实例,真正的代表一个可绘制对象。

RenderObject树

每个RenderObject类似于安卓中的View,每个对象都对应一个被绘制在屏幕上的组件。每次组件更新时都会进行根据属性判断RenderObject是否需要重绘,如果不需要直接复用,避免重绘。

Widget、Element、RenderObject

下面是Widget的继承体系,我们组常使用的是StatelessWidget和StatefulWidget,RenderObjectWidget才是组件库的核心,负责提供可绘制的组件,如:文字、颜色、布局等

不同类型组件创建的Element也不一样,Element继承体系,

组件重建过程

drawFrame()->BuildOwner.buildScope()->

setState流程

//State.setState

void setState(VoidCallback fn) {

  //assert...

  final Object? result = fn() as dynamic;

  // assert,保证result不是future,等价于: result is not Future

  _element!.markNeedsBuild();

}





void markNeedsBuild() {

  if (_lifecycleState != _ElementLifecycle.active)

    return;

  //assert...

  if (dirty)

    return;

  _dirty = true;

  owner!.scheduleBuildFor(this);

}



//BuildOwner

void scheduleBuildFor(Element element) {

  //assert...

  if (element._inDirtyList) {

    //assert...

    _dirtyElementsNeedsResorting = true;

    return;

  }

  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {

    _scheduledFlushDirtyElements = true;

    onBuildScheduled!();

  }

  _dirtyElements.add(element);

  element._inDirtyList = true;

  //assert...

}

略去assert函数,setState函数很简单,调用回调函数,将组件标记为需要重建。等待下次引擎调用drawFrame时会通过owner.buildScope()来重建对应的组件树。

drawFrame

负责绘制需要更新的组件,由

//WidgetsBinding.drawFrame

void drawFrame() {

  //assert...

  //...

  try {

    if (renderViewElement != null)

      buildOwner!.buildScope(renderViewElement!);

    //这里对应RenderBinding.drawFrame();  

    super.drawFrame();

    buildOwner!.finalizeTree();

  } finally {

    //assert...

  }

  //...

}



//RendingBinding.drawFrame

void drawFrame() {

  assert(renderView != null);

  //重新layout

  pipelineOwner.flushLayout();

  //重新合成

 pipelineOwner.flushCompositingBits();

  //重绘组件

  pipelineOwner.flushPaint();

  if (sendFramesToEngine) {

    renderView.compositeFrame(); // this sends the bits to the GPU

 pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.

 _firstFrameSent = true;

  }

}

PipelineOwner中有_nodesNeedingLayout、_nodesNeedingCompositingBitsUpdate、_nodesNeedingPaint三个队列分别保存下一次绘制前需要重绘的布局和组件,下次绘制时调用drawFrame,上面的三个方法分别对应三个队列的处理。

buildScope重建Element树

buildScope会扫描dirtyList中的元素,调用rebuild()来重新构建。



void buildScope(Element context, [ VoidCallback? callback ]) {

  if (callback == null && _dirtyElements.isEmpty)

    return;

  //assert...

  Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent);

  try {

    _scheduledFlushDirtyElements = true;

    if (callback != null) {

      //assert...

      _dirtyElementsNeedsResorting = false;

      try {

        //执行回调

        callback();

      } finally {

        //assert...

      }

    }

    _dirtyElements.sort(Element. _sort);

    _dirtyElementsNeedsResorting = false;

    int dirtyCount = _dirtyElements.length;

    int index = 0;

    while (index < dirtyCount) {

      //assert...

      try {

        //调用element的rebuild

        _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) {

          // It is possible for previously dirty but inactive widgets to move right in the list.

 // We therefore have to move the index left in the list to account for this.

 // We don't know how many could have moved. However, we do know that the only possible

 // change to the list is that nodes that were previously to the left of the index have

 // now moved to be to the right of the right-most cleaned node, and we do know that

 // all the clean nodes were to the left of the index. So we move the index left

 // until just after the right-most clean node.

 index -= 1;

        }

      }

    }

    //assert...

  } finally {

    for (final Element element in _dirtyElements) {

      assert(element._inDirtyList);

      element._inDirtyList = false;

    }

    _dirtyElements.clear();

    _scheduledFlushDirtyElements = false;

    _dirtyElementsNeedsResorting = null;

    Timeline.finishSync();

    //assert...

  }

  //assert...

}

Element的rebuild过程

rebuild直接调用对应element的performRebuild方法,该方法是一个抽象方法,不同的element实现方式不一样,我们只关注三个:ComponentElement、StatefulElement、RenderObjectElement



void rebuild() {

  //assert...

  //不可见和非标记为dirty的组件不会重新build

  if (_lifecycleState != _ElementLifecycle.active || !_dirty)

    return;

  //assert...

  performRebuild();

  //assert...

}

StatefulElement

重建过程和ComponentElement差不读,只不过做了一些判断调用了

void performRebuild() {

  if (_didChangeDependencies) {

    state.didChangeDependencies();

    _didChangeDependencies = false;

  }

  //ComponentElement.performRebuild

  super.performRebuild();

}

ComponentElement



void performRebuild() {

  //...

  Widget? built;

  try {

    //assert...

    //调用build方法,重新构建组件

    built = build();

    //assert...

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

    //assert...

  }

  try {

    _child = updateChild(_child, built, slot);

    //assert...

  } catch (e, stack) {

    //assert...

    _child = updateChild(null, built, slot);

  }

  //...

}

updateChild方法根据child和newWidget的参数是否为null采取不一样的功能。

  1. child != null, newWidget = null,卸载原组件
  2. child = null, newWidget != null, 创建新的element,用newWidget去配置element
  3. child != null, newWidget != null,更新组件


Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {

  if (newWidget == null) {

    if (child != null)

      //卸载组件

      deactivateChild(child);

    return null;

  }

  final Element newChild;

  if (child != null) {

    bool hasSameSuperclass = true;

 //assert...

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

      //assert...

      newChild = child;

    } else {

      deactivateChild(child);

      //assert...

      newChild = inflateWidget(newWidget, newSlot);

    }

  } else {

    newChild = inflateWidget(newWidget, newSlot);

  }

  //assert...



  return newChild;

}



//Widget.canUpdate

static bool canUpdate(Widget oldWidget, Widget newWidget) {

  return oldWidget.runtimeType == newWidget.runtimeType

      && oldWidget.key == newWidget.key;

}

更新组件时还要用Widget.canUpdate(child.widget, newWidget)去判断是否可以更新,其方法很简单,就是判断一下类型和Key;

其中最关键的在于Element.update方法,其会对子组件进行更新:

void update(covariant Widget newWidget) {

  //assert...

  _widget = newWidget;

}

默认就是一个简单的组件赋值,但是子类会进行重写。

StatelessElement.update:

无状态组件就是更新自身后再rebuild子组件,一直更新组件树。

void update(StatelessWidget newWidget) {

  super.update(newWidget);

  assert(widget == newWidget);

  _dirty = true;

  //rebuild子组件

  rebuild();

}

StatefulElement.update:

这里就是官方文档说的,StatefulWidget每次修改会保持state对象,只重建Widget。



void update(StatefulWidget newWidget) {

  super.update(newWidget);

  assert(widget == newWidget);

  final StatefulWidget oldWidget = state._widget!;

  // We mark ourselves as dirty before calling didUpdateWidget to

 // let authors call setState from within didUpdateWidget without triggering

 // asserts.

 _dirty = true;

  //保持state不变,重新修改widget

  state._widget = widget as StatefulWidget;

  try {

    _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);

    final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;

    //assert

  } finally {

    _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);

  }

  //build子组件

  rebuild();

}

RenderObjectElement.update:

ComponentElement只是对其它Element的组合,本身不代表实际渲染的组件,所有更新时只要更新子组件即可。

而RenderObjectElement代表一个真正需要绘制的Element。更新时就要判断该RenderObject是否需要重新绘制,也就是我们说的diff过程,下面会详细说。

void update(covariant RenderObjectWidget newWidget) {

  //注意,这里的super是Element

  super.update(newWidget);

  //assert...

  //关键

  widget.updateRenderObject(this, renderObject);

  //asert...

  _dirty = false;

}

子类还会对该方法进行扩展,如:SingleChildRenderObjectElement还继续会更新子组件。

void update(SingleChildRenderObjectWidget newWidget) {

  super.update(newWidget);

  assert(widget == newWidget);

  _child = updateChild(_child, widget.child, null);

}

其中的关键在于updateRenderObject,下面会说。

RenderObjectElement:

RenderObjectElement的performRebuild

void performRebuild() {

  //assert...

  widget.updateRenderObject(this, renderObject);

  //assert...

  _dirty = false;

}

注意,这里还调用了widget.updateRenderObject(),这也是一个抽象方法,不同的子类根据自身实现,这里才是进行diff的关键。

我们以Padding为例:

//padding继承自SingleChildRenderObjectWidget

class Padding extends SingleChildRenderObjectWidget 



void updateRenderObject(BuildContext context, RenderPadding renderObject) {

//用自身widget的配置去更新renderObject

  renderObject

    ..padding = padding

 ..textDirection = Directionality. maybeOf (context);

}

这里不仅仅是简单的赋值,RenderPadding的set方法如下:

如果padding的值改变,就将自身标记为需要重绘,添加到PipelineOwner的

_nodesNeedingLayout中,类似的还有_nodesNeedingPaint队列。下次drawFrame时会重绘这些组件。

set padding(EdgeInsetsGeometry value) {

  assert(value != null);

  assert(value.isNonNegative);

  //如果一致直接返回

  if (_padding == value)

    return;

  _padding = value;

  //标记为需要更新

  _markNeedResolution();

}



void _markNeedResolution() {

  _resolvedPadding = null;

  markNeedsLayout();

}





void markNeedsLayout() {

  if ((_cachedBaselines != null && _cachedBaselines!.isNotEmpty) ||

      (_cachedIntrinsicDimensions != null && _cachedIntrinsicDimensions!.isNotEmpty) ||

      (_cachedDryLayoutSizes != null && _cachedDryLayoutSizes!.isNotEmpty)) {

    // If we have cached data, then someone must have used our data.

 // Since the parent will shortly be marked dirty, we can forget that they

 // used the baseline and/or intrinsic dimensions. If they use them again,

 // then we'll fill the cache again, and if we get dirty again, we'll

 // notify them again.

 _cachedBaselines?.clear();

    _cachedIntrinsicDimensions?.clear();

    _cachedDryLayoutSizes?.clear();

    if (parent is RenderObject) {

      markParentNeedsLayout();

      return;

    }

  }

  super.markNeedsLayout();

}
//RenderObject.markNeedsLayout

void markNeedsLayout() {

  assert(_debugCanPerformMutations);

  if (_needsLayout) {

    assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());

    return;

  }

  assert(_relayoutBoundary != null);

  if (_relayoutBoundary != this) {

    markParentNeedsLayout();

  } else {

    _needsLayout = true;

    if (owner != null) {

      assert(() {

        if (debugPrintMarkNeedsLayoutStacks)

          debugPrintStack(label: 'markNeedsLayout() called for $this');

        return true;

      }());

      //添加到需要重绘的队列中

      owner!._nodesNeedingLayout.add(this);

      //通知有RenderObject需要重绘

      owner!.requestVisualUpdate();

    }

  }

}