接上篇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也不会刷新