Flutter Framework层UI绘制super.drawFrame()方法(三)

Flutter学习簿

共 9466字,需浏览 19分钟

 ·

2021-05-26 12:16

本文记录自己学习 Flutter 绘制所见源码,并不是全面详细的解析 Blog。

Flutter 源码环境:1.22.1

//此方法由[handleDrawFrame]调用,当需要布置和绘制框架时,引擎会自动调用该方法。@override  void drawFrame() {    ...    try {      if (renderViewElement != null)      // 1. 调用 buildScope()        buildOwner.buildScope(renderViewElement);        //2.调用super.drawFrame();      super.drawFrame();      // 3. 调用finalizeTree()      buildOwner.finalizeTree();    } finally {      ...    }    ...  }

上篇学习了 buildScope()方法主要是刷新绘制控件的所需参数,我们在看看 super.drawFrame()这个方法干了些什么?

flutter/lib/src/rendering/binding.dart

  @protected  void drawFrame() {    assert(renderView != null);    //调用2.1    pipelineOwner.flushLayout();    //调用2.2    pipelineOwner.flushCompositingBits();    //调用2.3    pipelineOwner.flushPaint();    if (sendFramesToEngine) {      //调用2.4      renderView.compositeFrame(); // this sends the bits to the GPU      //调用2.5      pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.      _firstFrameSent = true;    }  }

机翻

  • 布局阶段:对系统中所有脏的[RenderObject]进行布局(请参阅[RenderObject.performLayout])。有关将对象标记为布局不干净的更多详细信息,请参见[RenderObject.markNeedsLayout]

  • 合成位阶段: 系统中任何脏[RenderObject] 对象的合成位都将会被更新,参考 [RenderObject.markNeedsCompositingBitsUpdate]

  • 绘制阶段:重新绘制系统中所有脏的[RenderObject](请参阅[RenderObject.paint])。这阶段会生成[Layer]树。关于更多将对象标记为脏画的详细信息请查阅[RenderObject.markNeedsPaint]方法。

  • 合成阶段:将 Layer Tree 变成[场景]并发送到 GPU,成像。

  • 语义阶段:系统中所有脏的[RenderObject]的语义都会被更新。并将语义发送给操作系统,这将生成[SemanticsNode]树。有关将对象标记为语义脏的更多详细信息,请参见[RenderObject.markNeedsSemanticsUpdate]。

2.1 pipelineOwner.flushLayout()

flutter/lib/src/rendering/object.dart

  void flushLayout() {    if (!kReleaseMode) {      Timeline.startSync('Layout', arguments: timelineArgumentsIndicatingLandmarkEvent);    }    try {      //遍历所有 需要更新多的RenderObject 对象      while (_nodesNeedingLayout.isNotEmpty) {        final List<RenderObject> dirtyNodes = _nodesNeedingLayout;        _nodesNeedingLayout = <RenderObject>[];        //进行排序,把depth 深度越小的对象排在越前面        for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {          if (node._needsLayout && node.owner == this)          //看方法名就能猜到,对RenderObject对象重新进行Layout          // 调用 2.1.1            node._layoutWithoutResize();        }      }    } finally {      if (!kReleaseMode) {        Timeline.finishSync();      }    }  }

该方法将所有所需更新的节点进行排序后,依次调用 node._layoutWithoutResize()进行重新 layout。

2.1.1 node._layoutWithoutResize()

flutter/lib/src/rendering/object.dart

 void _layoutWithoutResize() {    assert(_relayoutBoundary == this);    RenderObject? debugPreviousActiveLayout;    ...    try {      //执行布局操作,具体的控件中会重写该方法      performLayout();      markNeedsSemanticsUpdate();    } catch (e, stack) {      _debugReportException('performLayout', e, stack);    }    ...    _needsLayout = false;    //调用 2.1.2    markNeedsPaint();  }

该方法在调用 performLayout()方法后,接着会调起 RenderObject.markNeedsPaint 方法,也就是说每次重新 layout 都会触发一次 paint。

2.1.2

flutter/lib/src/rendering/object.dart

void markNeedsPaint() {
if (_needsPaint) return; _needsPaint = true; if (isRepaintBoundary) {
if (owner != null) { owner!._nodesNeedingPaint.add(this); owner!.requestVisualUpdate(); } } else if (parent is RenderObject) { final RenderObject parent = this.parent as RenderObject; parent.markNeedsPaint(); } else { // If we're the root of the render tree (probably a RenderView), // 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. if (owner != null) owner!.requestVisualUpdate(); } }

该方法先判断当前的 RenderObject.isRepaintBoundary 是否为 true,如果是则把当前 RenderObject 加入到_nodesNeedingPaint 列表中等待接下来的 flushPaint 处理,并触发下一帧的绘制;当 isRepaintBoundary 不为 true 的时候,则会一直往上查找直到找到 isRepaintBoundary 为 true 的 RenderObject,也就是有可能会找到根节点 RenderView,然后加入到_nodesNeedingPaint 列表中。

因此,对于经常需要重绘区域,最好把 isRepaintBoundary 标记 true,这样就尽量避免触发全局重绘,提高性能,对应的 flutter 就已经提供了一个 RepaintBoundary 控件,自动把 isRepaintBoundary 标记为 true,非常方便我们去做优化。

2.2 pipelineOwner.flushCompositingBits()

flutter/lib/src/rendering/object.dart

  void flushCompositingBits() {    if (!kReleaseMode) {      Timeline.startSync('Compositing bits');    }    _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);    for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) {      //根据需要来决定是否更新位合成      if (node._needsCompositingBitsUpdate && node.owner == this)        node._updateCompositingBits();//调用2.2.1    }    ////清空需要位合成的渲染对象    _nodesNeedingCompositingBitsUpdate.clear();    if (!kReleaseMode) {      Timeline.finishSync();    }  }

2.2.1 node._updateCompositingBits()

flutter/lib/src/rendering/object.dart

  void _updateCompositingBits() {    if (!_needsCompositingBitsUpdate)      return;    final bool oldNeedsCompositing = _needsCompositing;    _needsCompositing = false;    visitChildren((RenderObject child) {      //遍历所有子项来更新位合成      child._updateCompositingBits();      if (child.needsCompositing)        _needsCompositing = true;    });    if (isRepaintBoundary || alwaysNeedsCompositing)      _needsCompositing = true;    if (oldNeedsCompositing != _needsCompositing)      markNeedsPaint();    _needsCompositingBitsUpdate = false;  }

2.3 pipelineOwner.flushPaint()

flutter/lib/src/rendering/object.dart

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._layer != null);        if (node._needsPaint && node.owner == this) {
//此节点是否连接到树中,如果连接则重绘,否则跳过 if (node._layer!.attached) { //调用2.3.1 PaintingContext.repaintCompositedChild(node); } else { node._skippedPaintingOnLayer(); } } } } finally { if (!kReleaseMode) { Timeline.finishSync(); } } }

该方法也是遍历排序,但是这里是从从节点最深的 RenderObject 开始绘制。

2.3.1 PaintingContext.repaintCompositedChild(node)

flutter/lib/src/rendering/object.dart

  static void _repaintCompositedChild(    RenderObject child, {    bool debugAlsoPaintedParent = false,    PaintingContext? childContext,  }) {    ...    OffsetLayer? childLayer = child._layer as OffsetLayer?;    if (childLayer == null) {
// Not using the `layer` setter because the setter asserts that we not // replace the layer for repaint boundaries. That assertion does not // apply here because this is exactly the place designed to create a // layer for repaint boundaries. child._layer = childLayer = OffsetLayer(); } else {
childLayer.removeAllChildren(); } childContext ??= PaintingContext(child._layer!, child.paintBounds); //调用2.3.2 绘制 child._paintWithContext(childContext, Offset.zero);
// Double-check that the paint method did not replace the layer (the first // check is done in the [layer] setter itself). childContext.stopRecordingIfNeeded(); }

2.3.2 _paintWithContext

flutter/lib/src/rendering/object.dart

  void _paintWithContext(PaintingContext context, Offset offset) {
if (_needsLayout) return; RenderObject? debugLastActivePaint;
_needsPaint = false; try { //调用 paint 开始绘制 paint(context, offset);
} catch (e, stack) { _debugReportException('paint', e, stack); } }

由此可见 flushPaint() 方法最终会调用到 paint 方法进行界面绘制。

2.4 compositeFrame()

flutter/lib/src/rendering/view.dart

  void compositeFrame() {    Timeline.startSync('Compositing', arguments: timelineArgumentsIndicatingLandmarkEvent);    try {      final ui.SceneBuilder builder = ui.SceneBuilder();      final ui.Scene scene = layer!.buildScene(builder);      if (automaticSystemUiAdjustment)        _updateSystemChrome();      _window.render(scene);      scene.dispose();      assert(() {        if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)          debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);        return true;      }());    } finally {      Timeline.finishSync();    }  }

该方法主要工作:

  • 分别创建 Flutter 框架(dart)和引擎层(C++)的两个 SceneBuilder;

  • 分别创建 Flutter 框架(dart)和引擎层(C++)的两个 Scene;

  • 执行 render()将 layer 树发送给 GPU 线程;

2.5 flushSemantics()

flutter/lib/src/rendering/object.dart

 void flushSemantics() {    if (_semanticsOwner == null)      return;    if (!kReleaseMode) {      Timeline.startSync('Semantics');    }    try {      final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()        ..sort((RenderObject a, RenderObject b) => a.depth - b.depth);      _nodesNeedingSemantics.clear();      //遍历_nodesNeedingSemantics,更新需要更新语义的渲染对象      for (final RenderObject node in nodesToProcess) {        if (node._needsSemanticsUpdate && node.owner == this)         //调用 更新语义          node._updateSemantics();      }      //发送语义更新      _semanticsOwner!.sendSemanticsUpdate();    } finally {      if (!kReleaseMode) {        Timeline.finishSync();      }    }  }

由上可知,super.drawFrame()主要做了更新 布局,合成,绘制,合成,语义阶段的功能。


浏览 39
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报