視圖樹的建立與管理機制、佈局、渲染核心框架node
調用runApp(rootWidget),將rootWidget傳給rootElement,作爲rootElement的子節點,生成Element樹,由Element樹生成Render樹算法
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
複製代碼
runApp(rootWidget) => attachRootWidget(rootWidget) => attachToRenderTree() => element.mount() => _rebuild() => updateChild()緩存
WidgetsFlutterBinding混入了很多的其餘的Bindingbash
持有BuildOwner、PipelineOwnerapp
BuildOwner框架
BuildOwner是Widget framework的管理類, 該類跟蹤哪些小部件須要從新構建,並處理應用於整個小部件樹的其餘任務,好比管理樹的非活動元素列表less
PipelineOwneride
管理真正須要繪製的View, 對RenderObjectTree中發生變化節點的進行flush操做, 最後交給底層引擎渲染佈局
1.attachRootWidget(app) 方法建立了Root[Widget](也就是 RenderObjectToWidgetAdapter)ui
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
複製代碼
2.緊接着調用attachToRenderTree方法建立了 Root[Element]
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
if (element == null) {
owner.lockState(() {
element = createElement(); //建立rootElement
element.assignOwner(owner); //綁定BuildOwner
});
owner.buildScope(element, () { //子widget的初始化從這裏開始
element.mount(null, null); // 初始化子Widget前,先執行rootElement的mount方法
});
} else {
...
}
return element;
}
複製代碼
3.Root[Element]嘗試調用mount方法將本身掛載到父Element上,由於本身就是root了,因此沒有父Element,掛空了
owner.buildScope(element, () { //子widget的初始化從這裏開始
element.mount(null, null); // 初始化子Widget前,先執行rootElement的mount方法
});
複製代碼
4.mount的過程當中會調用Widget的createRenderObject,建立了 Root[RenderObject]
void mount(Element parent, dynamic newSlot) {
_parent = parent; //持有父Element的引用
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;//當前節點的深度
_active = true;
if (parent != null) // Only assign ownership if the parent is non-null
_owner = parent.owner; //每一個Element的buildOwner,都來自父類的BuildOwner, 這樣能夠保證一個ElementTree,只由一個BuildOwner來維護
...
}
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
複製代碼
5.咱們將app做爲參數傳給了Root[Widget](也就是 RenderObjectToWidgetAdapter),app[Widget]也就成了爲root[Widget]的child[Widget]
6.調用owner.buildScope,開始執行子Tree的建立以及掛載(與更新流程一致, 見更新)
7.調用createElement方法建立出Child[Element]
Element inflateWidget(Widget newWidget, dynamic newSlot) {
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild;
}
}
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
複製代碼
8.調用Element的mount方法,將本身掛載到Root[Element]上,造成一棵樹
9.掛載的同時,調用widget.createRenderObject,建立Child[RenderObject]
10.建立完成後,調用attachRenderObject,完成和Root[RenderObject]的連接
@override
void attachRenderObject(dynamic newSlot) {
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
final ParentDataElement<RenderObjectWidget> parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
複製代碼
RenderObject與父RenderObject的掛載稍微複雜了點。每個Widget都有一個對應的Element,但Element不必定會有對應的RenderObject。(由於有一些Element是不用來作頁面顯示的, 像StatelessWidget=>StatelessElement沒有對應的RenderObject)因此你的父Element並不一有RenderObject,這個時候就須要向上查找。
RenderObjectElement _findAncestorRenderObjectElement() {
Element ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement)
ancestor = ancestor._parent;
return ancestor;
}
複製代碼
find方法在向上遍歷Element,直到找到RenderObjectElement,RenderObjectElement確定是有對應的RenderObject了,這個時候在進行RenderObject子父間的掛載。
安排一個幀儘快運行, 這在應用程序啓動期間使用,以便第一個幀(可能很是昂貴)能夠多運行幾毫秒。(我的理解是爲了實現第一次頁面渲染能夠調用到 => drawFrame)
@protected
void setState(VoidCallback fn) {
...
_element.markNeedsBuild();
}
複製代碼
void markNeedsBuild() {
...
_dirty = true; // 標記自身爲dirty
owner.scheduleBuildFor(this); // 通知buildOwner處理
}
複製代碼
buildOwner會將全部dirty的Element添加到_dirtyElements當中,等待下一幀繪製時集中處理。
還會調用ui.window.scheduleFrame();通知底層渲染引擎安排新的一幀處理。
void scheduleBuildFor(Element element) {
...
_dirtyElements.add(element);
element._inDirtyList = true;
...
}
複製代碼
void drawFrame() {
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
}
...
}
複製代碼
void buildScope(Element context, [VoidCallback callback]) {
...
try {
...
//1.排序
_dirtyElements.sort(Element._sort);
...
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
try {
//2.遍歷rebuild
_dirtyElements[index].rebuild();
} catch (e, stack) {
}
index += 1;
}
} finally {
for (Element element in _dirtyElements) {
element._inDirtyList = false;
}
//3.清空
_dirtyElements.clear();
...
}
}
複製代碼
遍歷執行的過程當中,也有可能會有新的element被加入到_dirtyElements集合中,此時會根據dirtyElements集合的長度判斷是否有新的元素進來了,若是有,就從新排序。
void rebuild() {
if (!_active || !_dirty)
return;
Element debugPreviousBuildTarget;
performRebuild();
}
複製代碼
element的rebuild方法最終會調用performRebuild(),而performRebuild()不一樣的Element有不一樣的實現
performRebuild()不一樣的Element有不一樣的實現,咱們暫時只看最經常使用的兩個Element:
ComponentElement,是StatefulWidget和StatelessElement的父類
void performRebuild() {
Widget built;
try {
built = build();
}
...
try {
_child = updateChild(_child, built, slot);
}
...
}
複製代碼
build()執行咱們複寫的StatefulWidget的state的build方法, 拿到子Widget, 交給updateChild
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
//1. 若是newWidget是null, 說明刪除控件, Element被刪除
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
if (child != null) {
//2. 若是新舊控件相同, 說明Widget複用了, 判斷位置是否相同, 不相同更新
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
//3. 判斷key值和運行時類型(runtimeType)是否相等, 都相同才能夠更新, 更新並返回Element(這個時候應該是Widget變了, 可是仍是同類型的Widget)
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);//
return child;
}
deactivateChild(child);
}
//4. 若是上面的條件都不知足, 建立新的Element
return inflateWidget(newWidget, newSlot);
}
複製代碼
1.若是newWidget是null, 說明刪除控件, Element被刪除
2.若是新舊控件相同, 說明Widget複用了, 判斷位置是否相同, 不相同更新
3.判斷key值和運行時類型(runtimeType)是否相等, 都相同才能夠更新, 更新並返回Element(這個時候應該是Widget變了, 可是仍是同類型的Widget)
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
複製代碼
child.update(newWidget);方法, 會根據newWidget的類型執行不一樣的update方法, 例如:
Column是MultiChildRenderObjectWidget類型的, 就會執行下面的方法:
@override
void update(MultiChildRenderObjectWidget newWidget) {
super.update(newWidget);
_children = updateChildren(_children, widget.children, forgottenChildren: _forgottenChildren);
_forgottenChildren.clear();
}
複製代碼
因爲Column裏面的孩子是children類型(MultiChildRenderObjectWidget), 有多個, 因此對比算法採用updateChildren, 返回新的Element
Container是StatelessWidget類型的, 因此他執行StatelessWidget的update方法:
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
_dirty = true;
rebuild();
}
複製代碼
Scaffold是StatefulWidget類型的, 因此執行:
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
final StatefulWidget oldWidget = _state._widget;
_dirty = true;
_state._widget = widget;
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
rebuild();
}
複製代碼
若是是StatelessWidget/StatefulWidget類型, 則繼續執行下一級的對比, 以此類推.(child.update => rebuild => performRebuild(), rebuild就是上面那個方法)
若是是MultiChildRenderObjectWidget類型, 則updateChildren裏面會進行List對比算法, 每個item也會調用updateChild()方法, 進行計算, 詳細過程見最後
4.若是上面的條件都不知足, 建立新的Element
首先會嘗試經過GlobalKey去查找可複用的Element,複用失敗就調用Widget的方法建立新的Element,而後調用mount方法,將本身掛載到父Element上去,會在這個方法裏建立新的RenderObject。
Element inflateWidget(Widget newWidget, dynamic newSlot) {
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild;
}
}
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
複製代碼
RenderObjectElement,是有渲染功能的Element的父類
與ComponentElement的不一樣之處在於,沒有去build,而是調用了updateRenderObject方法更新RenderObject。
@override
void performRebuild() {
widget.updateRenderObject(this, renderObject);
_dirty = false;
}
複製代碼
他表明的具備本身渲染功能的一類Widget(Text,沒有child的)
@protected
void drawFrame() {
pipelineOwner.flushLayout(); //佈局須要被佈局的RenderObject
pipelineOwner.flushCompositingBits(); // 判斷layer是否變化
pipelineOwner.flushPaint(); //繪製須要被繪製的RenderObject
renderView.compositeFrame(); // this sends the bits to the GPU 將畫好的layer傳給engine繪製
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. 一些語義場景須要
}
複製代碼
void finalizeTree() {
Timeline.startSync('Finalize tree', arguments: timelineWhitelistArguments);
try {
lockState(() {
_inactiveElements._unmountAll(); // this unregisters the GlobalKeys
});
...
} catch (e, stack) {
_debugReportException('while finalizing the widget tree', e, stack);
} finally {
Timeline.finishSync();
}
}
複製代碼
全部沒用的element都調用了deactivateChild方法進行回收
void deactivateChild(Element child) {
child._parent = null;
child.detachRenderObject();
owner._inactiveElements.add(child); // this eventually calls child.deactivate()
}
複製代碼
也就在這裏將被廢棄的element添加到了_inactiveElements當中。
另外在廢棄element以後,調用inflateWidget建立新的element時,還調用了_retakeInactiveElement嘗試經過GlobalKey複用element,此時的複用池也是在_inactiveElements當中。
若是你沒有在一幀裏經過GlobeKey完成Element的複用,_inactiveElements在最後將被清空,就沒辦法在複用了。
@protected
List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets, { Set<Element> forgottenChildren }) {
Element replaceWithNullIfForgotten(Element child) {
return forgottenChildren != null && forgottenChildren.contains(child) ? null : child;
}
int newChildrenTop = 0;
int oldChildrenTop = 0;
int newChildrenBottom = newWidgets.length - 1;
int oldChildrenBottom = oldChildren.length - 1;
final List<Element> newChildren = oldChildren.length == newWidgets.length ?
oldChildren : List<Element>(newWidgets.length);
Element previousChild;
// Update the top of the list.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
final Widget newWidget = newWidgets[newChildrenTop];
if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break;
final Element newChild = updateChild(oldChild, newWidget, previousChild);
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
oldChildrenTop += 1;
}
// Scan the bottom of the list.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
final Widget newWidget = newWidgets[newChildrenBottom];
if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break;
oldChildrenBottom -= 1;
newChildrenBottom -= 1;
}
// Scan the old children in the middle of the list.
final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
Map<Key, Element> oldKeyedChildren;
if (haveOldChildren) {
oldKeyedChildren = <Key, Element>{};
while (oldChildrenTop <= oldChildrenBottom) {
final Element oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
if (oldChild != null) {
if (oldChild.widget.key != null)
oldKeyedChildren[oldChild.widget.key] = oldChild;
else
deactivateChild(oldChild);
}
oldChildrenTop += 1;
}
}
// Update the middle of the list.
while (newChildrenTop <= newChildrenBottom) {
Element oldChild;
final Widget newWidget = newWidgets[newChildrenTop];
if (haveOldChildren) {
final Key key = newWidget.key;
if (key != null) {
oldChild = oldKeyedChildren[key];
if (oldChild != null) {
if (Widget.canUpdate(oldChild.widget, newWidget)) {
// we found a match!
// remove it from oldKeyedChildren so we don't unsync it later oldKeyedChildren.remove(key); } else { // Not a match, let's pretend we didn't see it for now. oldChild = null; } } } } final Element newChild = updateChild(oldChild, newWidget, previousChild); newChildren[newChildrenTop] = newChild; previousChild = newChild; newChildrenTop += 1; } // We've scanned the whole list.
newChildrenBottom = newWidgets.length - 1;
oldChildrenBottom = oldChildren.length - 1;
// Update the bottom of the list.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = oldChildren[oldChildrenTop];
final Widget newWidget = newWidgets[newChildrenTop];
final Element newChild = updateChild(oldChild, newWidget, previousChild);
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
oldChildrenTop += 1;
}
// Clean up any of the remaining middle nodes from the old list.
if (haveOldChildren && oldKeyedChildren.isNotEmpty) {
for (Element oldChild in oldKeyedChildren.values) {
if (forgottenChildren == null || !forgottenChildren.contains(oldChild))
deactivateChild(oldChild);
}
}
return newChildren;
}
複製代碼