开启生长之旅!这是我参加「日新计划 12 月更文应战」的第7天,点击检查活动详情

本文首要剖析了RendererBinding 的效果和内部一些重要的类. 希望此文能给你带来收成.

剖析

RendererBinding 的效果是担任render tree 和flutter engine之间的衔接. 咱们在发动App的时分,首先会创立 PiplineOwner ,然后经过platformDispatcher去监听屏幕分辨率变化、系统文字巨细变化、亮度、语义等等.最终去初始化RenderView,根据渠道去处理如帧回调、鼠标、web之类的信息.

 void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    platformDispatcher
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }

PipelineOwner

这儿咱们着重讲一下PipelineOwner, 官方描绘中有这么一句话The pipeline owner manages the rendering pipeline., 也便是说 PipelineOwner帮咱们管理着烘托所需求的. 咱们根据PipelineOwner调用的顺序顺次解说下它提供的方法.

flushLayout

这个阶段将核算每个烘托目标的巨细和位置, 烘托目标可能会在制作或许compositing state 的时分被标成dirty,这是什么意思呢? 让咱们回归到代码中

  // 持有需求被布局的目标
  List<RenderObject> _nodesNeedingLayout = <RenderObject>[];
{
    非release包运转代码忽略
    ...
    try {
      // 假如当时的节点需求组成
      while (_nodesNeedingLayout.isNotEmpty) {
        final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
        _nodesNeedingLayout = <RenderObject>[];
        for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
          if (node._needsLayout && node.owner == this)
            node._layoutWithoutResize();
        }
      }
    } finally {
        ... 
    }
}

代码中可以看到, 假如 _nodesNeedingLayout 中目标不为空.说明当时需求被布局,核算其巨细.咱们可以看到在顺次处理节点时,最终一步是履行 _layoutWithoutResize() ,这个方法调用的本质实际上也便是 performLayout(). 那么, performLayout() 做了什么呢? 假如对自定义布局有过了解, 通常咱们在完成 performLayout() 的时分.会先去 layout widget . 然后去经过position将widget 定位. 确定好widget在父widget中的相对位置.

flushCompositingBits

这个阶段中, 每个烘托目标都会了解其子目标是否需求组成.在制作的阶段选择如何完成视觉效果. 这儿实际上也便是标记所有的子目标是否需求组成

  void flushCompositingBits() {
    ...
    _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
    for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) {
      if (node._needsCompositingBitsUpdate && node.owner == this)
        node._updateCompositingBits();
    }
    _nodesNeedingCompositingBitsUpdate.clear();
   ...
  }

flushPaint

在这个阶段,咱们将会真正的制作出Layer

  void flushPaint() {
    ...
    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)) {
        ...
        if (node._needsPaint && node.owner == this) {
          if (node._layerHandle.layer!.attached) {
            PaintingContext.repaintCompositedChild(node);
          } else {
            node._skippedPaintingOnLayer();
          }
        }
      }
      ...
    } finally {
      ...
    }
  }

在制作的时分将会判断当时的layer是否attached,假如不是attched的状况.则说明当时的layer现已调用detach方法,因此不再需求制作.所以会跳过制作,履行 _skippedPaintingOnLayer() 的方法. 假如是attached的状况,则需求调用 repaintCompositedChild() 的方法

flushSemantics

最终,假如启用了语义. 这个阶段将会编译烘托目标的语义,这儿就不过多介绍了.

initRenderView

假如说还有比较重要的方法需求解说, 那么便是 initRenderView() 这个方法了.这儿将会创立一个 RenderView的目标作为RenderObject的根 ,同时对它进行初始化.

  void initRenderView() {
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
    // 预备第一帧发动烘托通道. 这儿只会调用一次.
    renderView.prepareInitialFrame();
  }

PrepareInitalFrame() 中, 咱们经过 scheduleInitialLayout和scheduleInitialPaint , 安排微事务队列尽可能快的处理layout和paint.

scheduleInitialLayout

在这个阶段,首要是将owner的_nodesNeedingLayout 目标中参加这个初始化的renderview.

scheduleInitialPaint

这个阶段中, 咱们将_layerHandle 中的layer 赋值成当时layer.并在owner中参加 _nodesNeedingPaint .

  void scheduleInitialPaint(ContainerLayer rootLayer) {
    _layerHandle.layer = rootLayer;
    owner!._nodesNeedingPaint.add(this);
  }

今天的RendererBinding源码剖析就暂告一个阶段了,它首要是担任了测量布局、制作之类的方法. 作为一个入口还是有了解的必要的, 建议大家有时间可以多看看.

源码剖析系列

Flutter图片缓存管理-ImageCache

Flutter runApp之GestureBinding

Flutter runApp 到烘托上屏

结语

这儿是WeninerIo,热爱生活且热爱游览.假如你对这次的共享感兴趣又或许有什么疑问, 不妨谈论区留言 + 关注.等待下一次更好的相遇.