flutter如何建立的檢視樹(WidgetTree),元素樹(ElementTree)及渲染樹(RenderingTree),又是如何更新檢視繪製檢視? 這個問題太大,剛開始一切又都是陌生的,理解起來千頭萬緒,所以先搞清這些樹的根結點的身份是非常必要的。毫無疑問,這些根節點的建立緊密的與初始化過程關聯,而確定了這些根節點之後,遍歷查詢更新就相對清晰了,因為繪製檢視無非也是對樹的遍歷查詢更新操作。
這部分就已經從引擎層進入到了dart層,需要了解的更多的是框架相關的機制,引擎目前用不到了。
環境: flutter sdk v1.7.8+hotfix.4@stable
先不要被Element
, RenderObjectElement
, RenderObject
, Widget
,RenderObjectWidget
諸多名稱嚇到。與安卓封裝了顯式的啟動執行過程不同,flutter有一個明確的runApp
, 這就是進行分析的方便入口。
語言機制
多繼承
需要先了解一下語言層面的一個多繼承機制。雖然這裡用了多繼承這個名詞,但是需要明確dart語言在語法上還是單繼承,也就是隻能extends
一個類,其它介面分別再以with
串接。
關鍵字宣告
與java不同,dart沒有interface
(準確的說是已移除)只有abstract
,abstract
的使用與java並無二致。沒有了interface
如何實現多介面物件的宣告?dart用的是mixin
關鍵字,所以就方便理解而言,把mixin
當作interface
, on
當作extends
(只針對mixin
類)即可。與interface
不同的是**mixin
宣告的類是可以有方法實現體和成員物件的**。
class A extends B implements C, D, E {}
class B {}
interface C {}
interface D {}
interface E {}
複製程式碼
dart等同於:
class A extends B with C, D, E {}
class B {}
mixin C {}
mixin D {}
mixin E {}
複製程式碼
繼承順序
在以上例子中假如B,C,D都有doSomeThing
方法
class A extends B with C, D {
@override
void doSomeThing() {
print("A");
super.doSomeThing();
}
}
class B {
@override
void doSomeThing() {
print("B");
}
}
mixin C on B {
@override
void doSomeThing() {
print("C");
super.doSomeThing();
}
}
mixin D on B {
@override
void doSomeThing() {
print("D");
super.doSomeThing();
}
}
void main() {
A().doSomeThing();
}
複製程式碼
那麼當執行A.doSomeThing
後應該是哪個呼叫順序?
直接給結論:以with宣告的反順序繼承
那麼問題來了:如果沒有C on B
會發生什麼?
語言機制問題可參考這篇文章。
串連呼叫
需要了解的第2個語法特性是串連呼叫,可以用..
操作符串連呼叫類的成員方法:
class F {
String str;
String contact(String s) {
return str + s;
}
void assign(String s) {
str = s;
}
}
void mai() {
F f = F()..assign("hello")..contact(' world');
print(f.str);
}
複製程式碼
需要明確:用了..
操作符之後呼叫返回的就是類物件例項,不再是方法的返回值。
初始化呼叫
有了以上基礎(用到語言特性1: mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding
)就可以理清runApp
入口的呼叫序列:
runApp
WidgetsFlutterBinding.ensureInitialized
WidgetsFlutterBinding()
BindingBase()
WidgetsBinding.initInstances
RendererBinding.initInstances
SemanticsBinding.initInstances
PaintingBinding.initInstances
SchedulerBinding.initInstances
ServicesBinding.initInstances
GestureBinding.initInstances
BindingBase.initInstances
複製程式碼
這裡包含了大量的資料初始化,用到一個找一個。 再看整體序列(widgets/binding.dart:786, 用到語言特性2):
runApp
WidgetsFlutterBinding.ensureInitialized
WidgetsBinding.attachRootWidget
WidgetsBinding.scheduleWarmUpFrame
複製程式碼
MyApp
例項被傳給了WidgetsBinding.attachRootWidget
方法,於是分析其呼叫序列:
runApp
WidgetsBinding.attachRootWidget
RenderObjectToWidgetAdapter()
RenderObjectToWidgetAdapter.attachToRenderTree
RenderObjectToWidgetAdapter.createElement
RenderObjectToWidgetElement<RenderBox>.assignOwner
BuildOwner.buildScope
RenderObjectToWidgetElement<RenderBox>.mount
複製程式碼
需要注意RenderObjectToWidgetAdapter
是一個RenderObjectWidget
型別,它用建構函式child: rootWidget,
持有了外部傳入的rootWidget
作為它的子檢視。
RenderObjectToWidgetAdapter.createElement
建立的元素被賦值給了_renderViewElement
,_renderViewElement
被WidgetsBinding
例項持有。
元素關聯渲染
那根渲染又是何時建立的呢?繼續看mount
的呼叫序列:
RenderObjectToWidgetElement<RenderBox>.mount
RootRenderObjectElement.mount
RenderObjectElement.mount
RenderObjectWidget.createRenderObject => RenderObjectToWidgetAdapter.createRenderObject
複製程式碼
這裡容易讓人誤導,呼叫createRenderObject
的其實是RenderObjectElement
持有的RenderObjectWidget
, 而元素RenderObjectToWidgetElement
正是RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this)
(widgets/binding.dart:833)所建立,這裡的this
其實就是RenderObjectToWidgetAdapter
,所以根渲染是RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
(widgets/bindings.836),可見根渲染不是在此時建立的,而是預先被賦值僅在此時返回的。
各種根節點
由此可見MyApp
作為外部傳入的rootWidget
不是真正的根檢視,真正的根檢視其實是RenderObjectToWidgetAdapter
, 它被RenderObjectToWidgetElement<RenderBox>
持有(一個Element持有一個Widget), 而這個Element被全域性WidgetsBinding
例項持有,所以根元素為RenderObjectToWidgetElement<RenderBox>
。
RenderObjectElement
在mount
的時機建立了一個RenderObject
例項並持有,而RenderObjectToWidgetElement
是RenderObjectElement
的子類,建立的RenderObject
具體型別為RenderObjectWithChildMixin<RenderBox>
,所以它才是最終的根渲染。
有了rootElement
就可以找到rootWidget
和rootRenderObject
, 元素樹,檢視樹與渲染樹由此建立起來。
根渲染建立
回到RenderObjectToWidgetAdapter
呼叫建構函式的地方,傳入的container
是RenderingBinding
的RenderView get renderView => _pipelineOwner.rootNode;
(rendering/binding.dart:162, attachRootWidget
是WidgetsBinding
的方法,但 mixin WidgetsBinding on RendererBinding
,所以可以引用到RenderingBinding
的成員)。
那麼rootRenderObject,也就是上面的RenderView
, 作為RenderObjectWithChildMixin<RenderBox>
的子類(class RenderView with RenderObjectWithChildMixin<RenderBox>
),又是在什麼時機建立的?跟蹤下來正是在初始化呼叫中:
runApp
WidgetsFlutterBinding.ensureInitialized
WidgetsFlutterBinding()
BindingBase()
WidgetsBinding.initInstances
RendererBinding.initInstances
_pipelineOwner = PipelineOwner(
RendererBinding.initRenderView
renderView = RenderView()
_pipelineOwner.rootNode = value;
複製程式碼
也就是說WidgetBinding把RendererBinding(mixin WidgetBinding with RendererBinding
)的renderView
作為了根渲染,而它實際是_pipelineOwner.rootNode
。
至此,我們便知道了所有節點遍歷的起點。