flutter-根视图根元素与根渲染

25次阅读

共计 4507 个字符,预计需要花费 12 分钟才能阅读完成。

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

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

正文完
 0