zoukankan      html  css  js  c++  java
  • flutter: 根视图、根元素与根渲染

    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(准确的说是已移除)只有abstractabstract的使用与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_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

    至此,我们便知道了所有节点遍历的起点。

  • 相关阅读:
    Python for Infomatics 第14章 数据库和SQL应用四(译)
    展望2017
    bing的简单英文字典工具
    自我安慰
    Python for Infomatics 第14章 数据库和SQL应用三(译)
    Python for Infomatics 第14章 数据库和SQL应用二(译)
    Python for Infomatics 第14章 数据库和SQL应用一(译)
    希望父亲早日恢复
    Python for Infomatics 第13章 网页服务四(译)
    Python for Infomatics 第13章 网页服务三(译)
  • 原文地址:https://www.cnblogs.com/lindeer/p/11296142.html
Copyright © 2011-2022 走看看