flutter: 根視圖、根元素與根渲染

flutter如何創建的視圖樹(WidgetTree),元素樹(ElementTree)及渲染樹(RenderingTree),又是如何更新視圖繪製視圖? 這個問題太大,剛開始一切又都是陌生的,理解起來千頭萬緒,因此先搞清這些樹的根結點的身份是很是必要的。毫無疑問,這些根節點的創建緊密的與初始化過程關聯,而肯定了這些根節點以後,遍歷查找更新就相對清晰了,由於繪製視圖無非也是對樹的遍歷查找更新操做。java

這部分就已經從引擎層進入到了dart層,須要瞭解的更多的是框架相關的機制,引擎目前用不到了。git

環境: flutter sdk v1.7.8+hotfix.4@stablegithub

先不要被Element, RenderObjectElement, RenderObject, Widget,RenderObjectWidget諸多名稱嚇到。與安卓封裝了顯式的啓動運行過程不一樣,flutter有一個明確的runApp, 這就是進行分析的方便入口。bash

語言機制

多繼承

須要先了解一下語言層面的一個多繼承機制。雖然這裏用了多繼承這個名詞,可是須要明確dart語言在語法上仍是單繼承,也就是隻能extends一個類,其它接口分別再以with串接。框架

關鍵字聲明

與java不一樣,dart沒有interface(準確的說是已移除)只有abstractabstract的使用與java並沒有二致。沒有了interface如何實現多接口對象的聲明?dart用的是mixin關鍵字,因此就方便理解而言,把mixin看成interface, on看成extends(只針對mixin類)便可。與interface不一樣的是**mixin聲明的類是能夠有方法實現體和成員對象的**。ide

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方法post

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會發生什麼? 語言機制問題可參考這篇文章ui

串連調用

須要瞭解的第2個語法特性是串連調用,能夠用..操做符串連調用類的成員方法:this

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_renderViewElementWidgetsBinding實例持有。

元素關聯渲染

那根渲染又是什麼時候建立的呢?繼續看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>

RenderObjectElementmount的時機建立了一個RenderObject實例並持有,而RenderObjectToWidgetElementRenderObjectElement的子類,建立的RenderObject具體類型爲RenderObjectWithChildMixin<RenderBox>,因此它纔是最終的根渲染。

有了rootElement就能夠找到rootWidgetrootRenderObject, 元素樹,視圖樹與渲染樹由此創建起來。

根渲染建立

回到RenderObjectToWidgetAdapter調用構造函數的地方,傳入的containerRenderingBindingRenderView get renderView => _pipelineOwner.rootNode;(rendering/binding.dart:162, attachRootWidgetWidgetsBinding的方法,但 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

至此,咱們便知道了全部節點遍歷的起點。

相關文章
相關標籤/搜索