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(准确的说是已移除)只有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, 元素树,视图树与渲染树由此建立起来。

    资源搜索网站大全 https://www.renrenfan.com.cn 广州VI设计公司https://www.houdianzi.com

    根渲染创建

    回到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。

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

  • 相关阅读:
    TCP和UDP的一些区别: TCP提供可靠传输的机制:
    rpc和 http的区别
    熔断原理与实现Golang版
    源码解读 Golang 的 sync.Map 实现原理
    mysql底层为啥用b 树不用红黑树_MySQL索引底层数据结构
    一条sql 查询语句是如何执行的
    网络相关知识
    为什么遍历 Go map 是无序的?
    Go语言 参数传递究竟是值传递还是引用传递的问题分析
    解决goland debug 调试问题 Version of Delve is too old for this version of Go
  • 原文地址:https://www.cnblogs.com/qianxiaox/p/14120502.html
Copyright © 2011-2022 走看看