深入理解Flutter 源码刷新机制------被遮挡路由如何被分离

接上篇https://blog.csdn.net/bawomingtian123/article/details/122407247

上一篇我们分析到旧路由被新的路由覆盖后,跟踪源码发现,即使旧的路由在定时调用setState 方法,由于Flutter刷新机制,该路由页面不会重新paint,从而达到高性能的目标。

那Flutter是如何做到被覆盖的路由即使调用setState也不被刷新呢,我们还是从上一篇文章结尾聊起

/// Update the display lists for all render objects.
  ///
  /// This function is one of the core stages of the rendering pipeline.
  /// Painting occurs after layout and before the scene is recomposited so that
  /// scene is composited with up-to-date display lists for every render object.
  ///
  /// See [RendererBinding] for an example of how this function is used.
  void flushPaint() {
    if (!kReleaseMode) {
      Timeline.startSync('Paint', arguments: timelineArgumentsIndicatingLandmarkEvent);
    }
    ...
    try {
      final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
      _nodesNeedingPaint = <RenderObject>[];
      // Sort the dirty nodes in reverse order (deepest first).
      for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
        assert(node._layerHandle.layer != null);
        if (node._needsPaint && node.owner == this) {
          if (node._layerHandle.layer!.attached) {
            PaintingContext.repaintCompositedChild(node);
          } else {
            node._skippedPaintingOnLayer();
          }
        }
      }
      assert(_nodesNeedingPaint.isEmpty);
    } finally {
      assert(() {
        _debugDoingPaint = false;
        return true;
      }());
      if (!kReleaseMode) {
        Timeline.finishSync();
      }
    }
  }

上面方法我们定位到当node._layerHandle.layer!.attached 条件不满足时,当前node就不会被paint

  /// Whether this node is in a tree whose root is attached to something.
  ///
  /// This becomes true during the call to [attach].
  ///
  /// This becomes false during the call to [detach].
  bool get attached => _owner != null;

也就是说当前node 的layer 的_owner 为空,就不会被paint

备注(作者在这里走了不少弯路,起先想追踪_owner 走什么逻辑在什么时刻被清空,花费了大把时间发现即使正常绘制的node,该字段也会不断被清空,而后重新赋值,由于Flutter 源码写的高级和作者水平有限,找来找去没有头绪,最后经过断点调试发现框架在每次刷新时,Node节点都会重新赋值,建立关联,走到这里有点柳暗花明)

我们接着讲

  void _compositeChild(RenderObject child, Offset offset) {
    assert(!_isRecording);
    assert(child.isRepaintBoundary);
    assert(_canvas == null || _canvas!.getSaveCount() == 1);

    // Create a layer for our child, and paint the child into it.
    if (child._needsPaint) {
      repaintCompositedChild(child, debugAlsoPaintedParent: true);
    } else {
      assert(() {
        // register the call for RepaintBoundary metrics
        child.debugRegisterRepaintBoundaryPaint();
        child._layerHandle.layer!.debugCreator = child.debugCreator ?? child;
        return true;
      }());
    }
    assert(child._layerHandle.layer is OffsetLayer);
    final OffsetLayer childOffsetLayer = child._layerHandle.layer! as OffsetLayer;
    childOffsetLayer.offset = offset;
    appendLayer(childOffsetLayer);
  }

断点调试发现,当新跳转到一个新路由时,根节点RenderView也会重新刷新,先前的RenderObjecTree也会重新刷新。这个方法的后面3行就是就是关键点,在刷新时Node创建新的layer,创建后将自己的layer 调用appendLayer关联到祖ContainerLayer

    final OffsetLayer childOffsetLayer = child._layerHandle.layer! as OffsetLayer;
    childOffsetLayer.offset = offset;
    appendLayer(childOffsetLayer);
  /// Adds a layer to the recording requiring that the recording is already
  /// stopped.
  ///
  /// Do not call this function directly: call [addLayer] or [pushLayer]
  /// instead. This function is called internally when all layers not
  /// generated from the [canvas] are added.
  ///
  /// Subclasses that need to customize how layers are added should override
  /// this method.
  @protected
  void appendLayer(Layer layer) {
    assert(!_isRecording);
    layer.remove();
    _containerLayer.append(layer);
  }


  /// Adds the given layer to the end of this layer's child list.
  void append(Layer child) {
    assert(child != this);
    assert(child != firstChild);
    assert(child != lastChild);
    assert(child.parent == null);
    assert(!child.attached);
    assert(child.nextSibling == null);
    assert(child.previousSibling == null);
    assert(child._parentHandle.layer == null);
    assert(() {
      Layer node = this;
      while (node.parent != null)
        node = node.parent!;
      assert(node != child); // indicates we are about to create a cycle
      return true;
    }());
    adoptChild(child);
    child._previousSibling = lastChild;
    if (lastChild != null)
      lastChild!._nextSibling = child;
    _lastChild = child;
    _firstChild ??= child;
    child._parentHandle.layer = child;
    assert(child.attached == attached);
  }

adoptChild(child) 这个方法就是将子节点挂在layer树上。具体挂载方法如下

  /// Mark the given node as being a child of this node.
  ///
  /// Subclasses should call this function when they acquire a new child.
  @protected
  @mustCallSuper
  void adoptChild(covariant AbstractNode child) {
    assert(child != null);
    assert(child._parent == null);
    assert(() {
      AbstractNode node = this;
      while (node.parent != null)
        node = node.parent!;
      assert(node != child); // indicates we are about to create a cycle
      return true;
    }());
    child._parent = this;
    if (attached)
      child.attach(_owner!);
    redepthChild(child);
  }

很明显在刷新时,RenderObjectTree各个节点的layer都会重新关联,断点调试发现,我们的第一个路由尽然没有在当前刷新RenderObjectTree上,怪不得node._layerHandle.layer!.attached不满足条件。

下一篇 我们接着分析为什么被遮挡的路由即使调用setState也不会刷新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值