Flutter开发进阶之瞧瞧RenderObject

Flutter开发进阶之瞧瞧RenderObject

通过上回我们了解到Flutter执行buildTree的逻辑线,当Tree构建完成后会交给Flutter底层的渲染事件循环去执行将内容渲染到屏幕的操作。
但是渲染的操作到底是如何串起来的呢?这篇文章将会从Element联系到RenderObject去瞧瞧逻辑线形成闭环。
Flutter开发进阶
在Element的源码中有一个方法,如下。

/*
### Rebuilding
  
  Dirty elements are rebuilt during the next frame. Precisely how this is
  done depends on the kind of element. A [StatelessElement] rebuilds by
  using its widget's [StatelessWidget.build] method. A [StatefulElement]
  rebuilds by using its widget's state's [State.build] method. A
  [RenderObjectElement] rebuilds by updating its [RenderObject].
  
  In many cases, the end result of rebuilding is a single child widget
  or (for [MultiChildRenderObjectElement]s) a list of children widgets.
  
  These child widgets are used to update the [widget] property of the
  element's child (or children) elements. The new [Widget] is considered to
  correspond to an existing [Element] if it has the same [Type] and [Key].
  (In the case of [MultiChildRenderObjectElement]s, some effort is put into
  tracking widgets even when they change order; see
  [RenderObjectElement.updateChildren].)
*/
('vm:prefer-inline')
void rebuild({bool force = false})

我们通过注释可知,Element在构建好Tree后,Dirty elements在下一帧重建,无论是多子Element还是单子Element都是通过RenderObjectElement更新它的RenderObject完成。
让我们来看看RenderObjectElementmount方法,以下。


  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    assert(() {
      _debugDoingBuild = true;
      return true;
    }());
    _renderObject = (widget as RenderObjectWidget).createRenderObject(this);
    assert(!_renderObject!.debugDisposed!);
    assert(() {
      _debugDoingBuild = false;
      return true;
    }());
    assert(() {
      _debugUpdateRenderObjectOwner();
      return true;
    }());
    assert(_slot == newSlot);
    attachRenderObject(newSlot);
    super.performRebuild(); // clears the "dirty" flag
  }

  void attachRenderObject(Object? newSlot) {
    assert(_ancestorRenderObjectElement == null);
    _slot = newSlot;
    _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
    _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
    final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
    if (parentDataElement != null) {
      _updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>);
    }
  }

RenderObjectElement装载RenderObject的过程中,会创建一个RenderObject与其对应,然后将RenderObject插入对应的卡槽(Slot),并且还需保证子RenderObject遵循上级Widget的相关配置。
根据事件线的逻辑,接下来将看到RenderObject的逻辑,源码代码太长就不一一贴了。

abstract class RenderObject
    with DiagnosticableTreeMixin
    implements HitTestTarget

RenderObject不仅负责Layout、Paint还需要负责hit,这里我们只考虑Layout和Paint
让我们看看markNeedsLayout,以下。

void markNeedsLayout() {
    assert(_debugCanPerformMutations);
    if (_needsLayout) {
      assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());
      return;
    }
    if (_relayoutBoundary == null) {
      _needsLayout = true;
      if (parent != null) {
        // _relayoutBoundary is cleaned by an ancestor in RenderObject.layout.
        // Conservatively mark everything dirty until it reaches the closest
        // known relayout boundary.
        markParentNeedsLayout();
      }
      return;
    }
    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);
        owner!.requestVisualUpdate();
      }
    }
  }

可以看到渲染的逻辑线是推迟到parent的,当合成的Layout需要执行渲染时,会交给owner并添加进需要Layout的集合里,然后通过owner!.requestVisualUpdate();去通知渲染管线进行渲染。
让我们看看源码中怎么说,以下。

/// The owner for this render object (null if unattached).
  ///
  /// The entire render tree that this render object belongs to
  /// will have the same owner.
  PipelineOwner? get owner => _owner;
  PipelineOwner? _owner;

可知在同一个Tree以下都对应同一个PipelineOwner,这个owner负责管理具体的渲染工作,相当于前文说到的ElementBuildOwner的关系。
接下来来我们看看markNeedsPaint方法,以下。

void markNeedsPaint() {
    assert(!_debugDisposed);
    assert(owner == null || !owner!.debugDoingPaint);
    if (_needsPaint) {
      return;
    }
    _needsPaint = true;
    // If this was not previously a repaint boundary it will not have
    // a layer we can paint from.
    if (isRepaintBoundary && _wasRepaintBoundary) {
      assert(() {
        if (debugPrintMarkNeedsPaintStacks) {
          debugPrintStack(label: 'markNeedsPaint() called for $this');
        }
        return true;
      }());
      // If we always have our own layer, then we can just repaint
      // ourselves without involving any other nodes.
      assert(_layerHandle.layer is OffsetLayer);
      if (owner != null) {
        owner!._nodesNeedingPaint.add(this);
        owner!.requestVisualUpdate();
      }
    } else if (parent is RenderObject) {
      parent!.markNeedsPaint();
    } else {
      assert(() {
        if (debugPrintMarkNeedsPaintStacks) {
          debugPrintStack(
              label: 'markNeedsPaint() called for $this (root of render tree)');
        }
        return true;
      }());
      // If we are the root of the render tree and not a repaint boundary
      // then we have to paint ourselves, since nobody else can paint us.
      // We don't add ourselves to _nodesNeedingPaint in this case,
      // because the root is always told to paint regardless.
      //
      // Trees rooted at a RenderView do not go through this
      // code path because RenderViews are repaint boundaries.
      if (owner != null) {
        owner!.requestVisualUpdate();
      }
    }
  }

markNeedsPaintRepaintBoundary会将渲染子Tree限制在自己的范围内,与markNeedsLayout类似,而且也会通过owner去执行渲染管线的管理。
让我们看看PipelineOwner是如何执行的?

/// Calls [onNeedVisualUpdate] if [onNeedVisualUpdate] is not null.
  ///
  /// Used to notify the pipeline owner that an associated render object wishes
  /// to update its visual appearance.
  void requestVisualUpdate() {
    if (onNeedVisualUpdate != null) {
      onNeedVisualUpdate!();
    } else {
      _manifold?.requestVisualUpdate();
    }
  }

在Flutter中,PipelineOwner负责渲染管线的各个方面,是具体与WidgetsBindingRendererBinding帧事件循环的交互。
让我们看看onNeedVisualUpdate的注释,以下。

/// Called when a render object associated with this pipeline owner wishes to
  /// update its visual appearance.
  ///
  /// Typical implementations of this function will schedule a task to flush the
  /// various stages of the pipeline. This function might be called multiple
  /// times in quick succession. Implementations should take care to discard
  /// duplicate calls quickly.
  ///
  /// When the [PipelineOwner] is attached to a [PipelineManifold] and
  /// [onNeedVisualUpdate] is provided, the [onNeedVisualUpdate] callback is
  /// invoked instead of calling [PipelineManifold.requestVisualUpdate].
  final VoidCallback? onNeedVisualUpdate;

可知作为一个调度任务可能被多次重复调用,当PipelineOwner连接到PipelineManifold并提供onNeedVisualUpdate时,会直接执行onNeedVisualUpdate
继续查看PipelineManifold,以下。

abstract class PipelineManifold implements Listenable {
  /// Whether [PipelineOwner]s connected to this [PipelineManifold] should
  /// collect semantics information and produce a semantics tree.
  ///
  /// The [PipelineManifold] notifies its listeners (managed with [addListener]
  /// and [removeListener]) when this property changes its value.
  ///
  /// See also:
  ///
  ///  * [SemanticsBinding.semanticsEnabled], which [PipelineManifold]
  ///    implementations typically use to back this property.
  bool get semanticsEnabled;

  /// Called by a [PipelineOwner] connected to this [PipelineManifold] when a
  /// [RenderObject] associated with that pipeline owner wishes to update its
  /// visual appearance.
  ///
  /// Typical implementations of this function will schedule a task to flush the
  /// various stages of the pipeline. This function might be called multiple
  /// times in quick succession. Implementations should take care to discard
  /// duplicate calls quickly.
  ///
  /// A [PipelineOwner] connected to this [PipelineManifold] will call
  /// [PipelineOwner.onNeedVisualUpdate] instead of this method if it has been
  /// configured with a non-null [PipelineOwner.onNeedVisualUpdate] callback.
  ///
  /// See also:
  ///
  ///  * [SchedulerBinding.ensureVisualUpdate], which [PipelineManifold]
  ///    implementations typically call to implement this method.
  void requestVisualUpdate();
}

在Flutter中,PipelineManifold通常不会暴露在外,它管理PipelineOwner Tree下所有PipelineOwner,它实现了PipelineOwner访问共享,PipelineManifold通过调用SchedulerBinding.ensureVisualUpdate实现通知渲染执行。
让我们继续进行下一步的探索,以下。

/// Schedules a new frame using [scheduleFrame] if this object is not
  /// currently producing a frame.
  ///
  /// Calling this method ensures that [handleDrawFrame] will eventually be
  /// called, unless it's already in progress.
  ///
  /// This has no effect if [schedulerPhase] is
  /// [SchedulerPhase.transientCallbacks] or [SchedulerPhase.midFrameMicrotasks]
  /// (because a frame is already being prepared in that case), or
  /// [SchedulerPhase.persistentCallbacks] (because a frame is actively being
  /// rendered in that case). It will schedule a frame if the [schedulerPhase]
  /// is [SchedulerPhase.idle] (in between frames) or
  /// [SchedulerPhase.postFrameCallbacks] (after a frame).
  void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

继续沿SchedulerBinding的执行路径,以下。

void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled) {
      return;
    }
    assert(() {
      if (debugPrintScheduleFrameStacks) {
        debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
      }
      return true;
    }());
    ensureFrameCallbacksRegistered();
    platformDispatcher.scheduleFrame();
    _hasScheduledFrame = true;
  }

最终void scheduleFrame()是具体下一帧绘制的方法,由此完成闭环。
综上所述,我们了解到,Flutter具体执行渲染时会构建RenderObject Tree(通过将RenderObject插入Slot),具体操作交给PipelineOwner管理,而同一Tree下的PipelineOwner对接唯一的PipelineManifold,最后通知SchedulerBinding去执行帧绘制的操作。

上一篇:node.js常用命令


下一篇:小程序应用市场的前世今生