文章目錄
- 背景
- 三棵樹的來源
- 時序圖
- attachRootWidget分析
- attachToRenderTree分析
- mount分析
- 總結
背景
相信寫過Flutter的開發同學都知道,我們的Flutter UI是由各色各樣的Widget組成,一般情況下很少只繪制單個widget的情況,那么如何有效的編排widget的結構,以及高效的將這些widget描述資訊交給Flutter Engine,從而渲染出可媲美原生UI的性能的,
本文我們就來探究下widget背后三棵樹的來龍去脈,
三棵樹的來源
從前面的文章"Flutter入口runApp原始碼分析"我們知道,在WidgrtsFlutterBinding初始化以后,接著就會呼叫到WidgetsBinding的scheduleAttachRootWidget方法,scheduleAttachRootWidget方法內部直接異步呼叫了attachRootWidget,這個方法就是三棵樹的起源,
直接上原始碼太枯燥,我們先來個時序圖,跟著主步驟一步步來,
時序圖

根據上面的時序圖我們能清楚的看到,關于Flutter widget三棵樹的起源就是在WidgetsBind的attachRootWidget方法之中,
attachRootWidget分析
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
// 創建RenderObjectToWidgetAdapter,RenderObjectToWidgetAdapter是RenderObjectElement和Element的橋梁,
// 第一步
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
//第二部
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
}
attachRootWidget方法很簡單,主要分為兩步:
- 創建RenderObjectToWidgetAdapter
- 呼叫RenderObjectToWidgetAdapter的attachToRenderTree方法
很明顯核心邏輯都在RenderObjectToWidgetAdapter,RenderObjectToWidgetAdapter,其實也是一個widget,初始化的時候將rootWidget傳遞過去,作為child,還有個引數就是renderView,實際上是一個RenderObject,而RenderObjectToWidgetAdapter作為一個adapter的主要作用就是建立RenderObject,Element,Widget之間的橋梁,
RenderView就是渲染樹的根,
剛剛講到這里RenderObjectToWidgetAdapter就已經持有了rootWidget和rootRender兩棵樹的根節點,下面我們再來看,attachToRenderTree方法,
attachToRenderTree分析
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
owner.lockState(() {
//創建rootElement
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
// 核心
element!.mount(null, null);
});
SchedulerBinding.instance!.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
這個方法的核心邏輯也比較清晰,首先來解釋下兩個引數:
-
BuildOwner owner
buildOwner來自WidgetsBinding初始化時實體化的BuildOwner實體
BuildOwner可以理解為widgets管理器,用于跟蹤哪些widget需要重建,并且處理適用于整體widget樹的其它任務,例如管理widget樹的非活動元素串列,以及除錯時熱多載期間辟謠時觸發
重新組裝指令,通常由WidgetsBind創建和擁有, -
RenderObjectToWidgetElement? element
element也就是
attachRootWidget傳遞過來的renderViewElement,其值就是_renderViewElement自己,此時由于呼叫完appach才賦值,所以首次進來也是null,首次進來為null的時候,就會進入到createElement()邏輯,也就是創建RootElement,
mount分析
我們繼續看Element的mount方法
@override
void mount(Element? parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
}
這個方法里面我們只需要關注createRenderObject方法,該方法通過呼叫RenderObjectToWidgetAdapter的createRenderObject回傳的其實就是RenderObjectToWidgetAdapter的container成員,也就是渲染樹的根節點,
其實講到這里widget三棵樹的根我們已經清楚了,對應為:
-
Widget 樹
根結點是 RenderObjectToWidgetAdapter(繼承自 RenderObjectWidget extends Widget),我們 runApp 中傳遞的 Widget 樹就被追加到了這個樹根的 child 屬性上,
-
Element 樹
根結點是 RenderObjectToWidgetElement(繼承自 RootRenderObjectElement extends RenderObjectElement extends Element),通過呼叫 RenderObjectToWidgetAdapter 的 createElement 方法創建,創建 RenderObjectToWidgetElement 的時候把 RenderObjectToWidgetAdapter 通過構造引數傳遞進去,所以 Element 的 _widget 屬性值為 RenderObjectToWidgetAdapter 實體,也就是說 Element 樹中 _widget 屬性持有了 Widget 樹實體,RenderObjectToWidgetAdapter ,
-
RenderObject 樹
-
根結點是 RenderView(RenderView extends RenderObject with RenderObjectWithChildMixin),在 Element 進行 mount 時通過呼叫 Widget 樹(RenderObjectToWidgetAdapter)的createRenderObject方法獲取 RenderObjectToWidgetAdapter 構造實體化時傳入的 RenderView 渲染樹根節點,
總結
上面就是Flutter UI三棵樹的誕生于關系,講到Widget,Element,RenderObject三者,雖然后兩者都是Widget通過createElement和createRenderObjet創建,但是管理三者關系的還是RenderObjectToWidgetElement,他們的結構關系是這個樣子
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/348429.html
標籤:其他
