在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