Flutter Framework层UI绘制super.drawFrame()方法(三)
本文记录自己学习 Flutter 绘制所见源码,并不是全面详细的解析 Blog。
Flutter 源码环境:1.22.1
//此方法由[handleDrawFrame]调用,当需要布置和绘制框架时,引擎会自动调用该方法。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
void drawFrame() {assert(renderView != null);//调用2.1pipelineOwner.flushLayout();//调用2.2pipelineOwner.flushCompositingBits();//调用2.3pipelineOwner.flushPaint();if (sendFramesToEngine) {//调用2.4renderView.compositeFrame(); // this sends the bits to the GPU//调用2.5pipelineOwner.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.1node._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.2markNeedsPaint();}
该方法在调用 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.1PaintingContext.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()主要做了更新 布局,合成,绘制,合成,语义阶段的功能。
