在Flutter框架分析(四)-RenderObject一文中,咱们简单介绍了RenderObject中一个重要成员变量:RelayoutBoundary。下面咱们简单回顾下RelayoutBoundary的主要做用。
当一个组件的大小被改变时,其parent的大小可能也会被影响,所以须要通知其父节点。若是这样迭代上去,须要通知整棵RenderObject Tree从新布局,必然会影响布局效率。所以,Flutter经过RelayoutBoundary将RenderObject Tree分段,若是遇到了RelayoutBoundary,则不去通知其父节点从新布局,由于其大小不会影响父节点的大小。这样就只须要对RenderObject Tree中的一段从新布局,提升了布局效率。
那么,RelayoutBoundary是怎么实现将RenderObject Tree分段的呢?本文将经过源码来剖析RelayoutBoundary的工做原理。node
在Flutter中,若是Widget有更新,须要从新布局,Framework会将须要布局的RenderObject加入PipelineOwner的_nodesNeedingLayout中,而后当下一个VSync信号来临时,Framework会遍历_nodesNeedingLayout,对其中的每个RenderObject从新进行布局,遍历_nodesNeedingLayout的函数源码以下:markdown
void flushLayout() {
try {
// TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themselves
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 {
}
}
复制代码
其中,_layoutWithoutResize会调用RenderObject的performLayout函数,实现该RenderObject的从新布局。
以上流程的示意图以下:架构
由上述逻辑可知,当Widget有更新,须要从新布局时,加入_nodesNeedingLayout的元素的多少直接关系到须要从新布局元素的多少,若是能将尽量少的RenderObject加入_layoutWithoutResize,便可尽量提升布局效率。这就是设计RelayoutBoundary的核心思路。
下面咱们来看何时会将RenderObject添加进_nodesNeedingLayout。从源码能够看到,添加进_nodesNeedingLayout有两个地方:框架
void scheduleInitialLayout() {
_relayoutBoundary = this;
owner._nodesNeedingLayout.add(this);
}
复制代码
本函数只在Flutter初始化的时候调用一次。函数
void markNeedsLayout() {
if (_needsLayout) {
return;
}
if (_relayoutBoundary != this) {
markParentNeedsLayout();
} else {
_needsLayout = true;
if (owner != null) {
owner._nodesNeedingLayout.add(this);
owner.requestVisualUpdate();
}
}
}
复制代码
那本函数的调用时机是什么呢?主要有如下几种:oop
当Flutter初始化进行第一次布局,每一个RenderObject均须要布局,所以无优化空间,本文主要关注对从新布局的优化,即对markNeedsLayout的调用。接下来咱们分析markNeedsLayout的调用链。其流程图以下:源码分析
可见,在一个RenderObject调用markNeedsLayout函数后,若是其自己不是_relayoutBoundary,则会经过markParentNeedsLayout函数调用到parent的markNeedsLayout函数,从而造成递归调用,直到找到最近的一个是_relayoutBoundary的上级节点,才会中止递归,并将该节点加入_nodesNeedingLayout。所以,经过_relayoutBoundary,Flutter将RenderObject Tree划分红了数段,当位于某段的RenderObject须要从新布局时,只会更新该段及其下的RenderObject,而不是整个RenderObject Tree。示意图以下:布局
那么,何时会将RenderObject设置为RelayoutBoundary呢?知足如下4种状况之一时,会将自身设置为RelayoutBoundary。post
以上条件很好理解,例如parentUsesSize = false,此时父节点的布局不依赖当前节点的大小,那当前节点布局更新天然不须要通知父节点,所以能够将其做为一个RelayoutBoundary。性能
本文首先介绍了RelayoutBoundary的做用,而后结合源码分析了RelayoutBoundary的做用原理,其重点以下:
Flutter框架分析(一)--架构总览
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject树
Flutter框架分析(六)-Constraint
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget