关于flutter:Flutter-的-runApp-与三棵树诞生流程源码分析

35次阅读

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

Flutter 系列文章连载~

  • 《Flutter Android 工程构造及应用层编译源码深入分析》
  • 《Flutter 命令实质之 Flutter tools 机制源码深入分析》
  • 《Flutter 的 runApp 与三棵树诞生流程源码剖析》
  • 《Flutter Android 端 Activity/Fragment 流程源码剖析》
  • 《Flutter Android 端 FlutterInjector 及依赖流程源码剖析》
  • 《Flutter Android 端 FlutterEngine Java 相干流程源码剖析》
  • 《Flutter Android 端 FlutterView 相干流程源码剖析》
  • 《Flutter 绘制动机 VSYNC 流程源码全方位剖析》
  • 《Flutter 安卓 Platform 与 Dart 端音讯通信形式 Channel 源码解析》

背景

从写 Flutter 第一行程序开始咱们就晓得在 Dart 的 main 办法中通过调用 runApp 办法把本人编写的 Widget 传递进去,只有这样编译运行后能力失去预期成果。你有没有好奇这背地都经验了什么?runApp 为什么这么神秘?或者说,在你入门 Flutter 后应该常常听到或看到过 Flutter 三棵树外围机制的货色,你有真正的想过他们都是什么吗?如果都没有,那么本文就是一场解密之旅。

Flutter 程序入口

咱们编写的 Flutter App 个别入口都是在 main 办法,其外部通过调用 runApp 办法将咱们本人整个利用的 Widget 增加并运行,所以咱们间接去看下 runApp 办法实现,如下:

/**
 * 地位:FLUTTER_SDK\packages\flutter\lib\src\widgets\binding.dart
 * 留神:app 参数的 Widget 布局盒子束缚 constraints 会被强制为填充屏幕,这是框架机制,本人想要调整能够用 Align 等包裹。* 多次重复调用 runApp 将会从屏幕上移除已增加的 app Widget 并增加新的下来,* 框架会对新的 Widget 树与之前的 Widget 树进行比拟,并将任何差别利用于底层渲染树,有点相似于 StatefulWidget
调用 State.setState 后的重建机制。*/
void runApp(Widget app) {WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();}

能够看到下面三行代码代表了 Flutter 启动的外围三步(级联运算符调用):

  1. WidgetsFlutterBinding 初始化(ensureInitialized()
  2. 绑定根节点创立外围三棵树(scheduleAttachRootWidget(app)
  3. 绘制热身帧(scheduleWarmUpFrame()

WidgetsFlutterBinding 实例及初始化

间接看源码,如下:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {static WidgetsBinding ensureInitialized() {if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance!;
  }
}

WidgetsFlutterBinding 继承自 BindingBase,并且 with 了大量的 mixin 类。WidgetsFlutterBinding 就是将 Widget 架构和 Flutter Engine 连贯的外围桥梁,也是整个 Flutter 的应用层外围。通过 ensureInitialized() 办法咱们能够失去一个全局单例的 WidgetsFlutterBinding 实例,且 mixin 的一堆 XxxBinding 也被实例化。

BindingBase 抽象类的构造方法中会调用 initInstances() 办法,而各种 mixin 的 XxxBinding 实例化重点也都在各自的 initInstances() 办法中,每个 XxxBinding 的职责不同,如下:

  • WidgetsFlutterBinding:外围桥梁主体,Flutter app 全局惟一。
  • BindingBase:绑定服务抽象类。
  • GestureBinding:Flutter 手势事件绑定,解决屏幕事件散发及事件回调解决,其初始化办法中重点就是把事件处理回调 _handlePointerDataPacket 函数赋值给 window 的属性,以便 window 收到屏幕事件后调用,window 实例是 Framework 层与 Engine 层解决屏幕事件的桥梁。
  • SchedulerBinding:Flutter 绘制调度器相干绑定类,debug 编译模式时统计绘制流程时长等操作。
  • ServicesBinding:Flutter 零碎平台音讯监听绑定类。即 Platform 与 Flutter 层通信相干服务,同时注册监听了利用的生命周期回调。
  • PaintingBinding:Flutter 绘制预热缓存等绑定类。
  • SemanticsBinding:语义树和 Flutter 引擎之间的粘合剂绑定类。
  • RendererBinding:渲染树和 Flutter 引擎之间的粘合剂绑定类,外部重点是持有了渲染树的根节点。
  • WidgetsBinding:Widget 树和 Flutter 引擎之间的粘合剂绑定类。

从 Flutter 架构宏观形象看,这些 XxxBinding 承当的角色大抵是一个桥梁关联绑定,如下:

本文因为是启动主流程相干机制剖析,所以初始化中咱们须要关注的次要是 RendererBinding 和 WidgetsBinding 类的 initInstances() 办法,如下:

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    ......
    /**
     *1、创立一个治理 Widgets 的类对象
     *BuildOwner 类用来跟踪哪些 Widget 须要重建,并解决用于 Widget 树的其余工作,例如治理不沉闷的 Widget 等,调试模式触发重建等。*/
    _buildOwner = BuildOwner();
    //2、回调办法赋值,当第一个可构建元素被标记为脏时调用。buildOwner!.onBuildScheduled = _handleBuildScheduled;
    //3、回调办法赋值,当本地配置变动或者 AccessibilityFeatures 变动时调用。window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    ......
  }
}

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    ......
    /**
     * 4、创立治理 rendering 渲染管道的类
     * 提供接口调用用来触发渲染。*/
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    //5、一堆 window 变动相干的回调监听
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    //6、创立 RenderView 对象,也就是 RenderObject 渲染树的根节点
    initRenderView();
    ......
  }

  void initRenderView() {
    ......
    //RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
    //7、渲染树的根节点对象
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
    renderView.prepareInitialFrame();}
  // 定义 renderView 的 get 办法,获取自_pipelineOwner.rootNode
  RenderView get renderView => _pipelineOwner.rootNode! as RenderView;
  // 定义 renderView 的 set 办法,下面 initRenderView()中实例化赋值就等于给_pipelineOwner.rootNode 也进行了赋值操作。set renderView(RenderView value) {assert(value != null);
    _pipelineOwner.rootNode = value;
  }
}

到此基于初始化过程咱们曾经失去了一些重要信息,请记住 RendererBinding 中的 RenderView 就是 RenderObject 渲染树的根节点。下面这部分代码的时序图大抵如下:

通过 scheduleAttachRootWidget 创立关联三棵外围树

WidgetsFlutterBinding 实例化单例初始化之后先调用了 scheduleAttachRootWidget(app) 办法,这个办法位于 mixin 的 WidgetsBinding 类中,实质是异步执行了 attachRootWidget(rootWidget) 办法,这个办法实现了 Flutter Widget 到 Element 到 RenderObject 的整个关联过程。源码如下:

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @protected
  void scheduleAttachRootWidget(Widget rootWidget) {
      // 简略的异步疾速执行,将 attachRootWidget 异步化
    Timer.run(() {attachRootWidget(rootWidget);
    });
  }

  void attachRootWidget(Widget rootWidget) {
      //1、是不是启动帧,即看 renderViewElement 是否有赋值,赋值机会为步骤 2
    final bool isBootstrapFrame = renderViewElement == null;
    _readyToProduceFrames = true;
    //2、桥梁创立 RenderObject、Element、Widget 关系树,_renderViewElement 值为 attachToRenderTree 办法返回值
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      //3、RenderObjectWithChildMixin 类型,继承自 RenderObject,RenderObject 继承自 AbstractNode。// 来自 RendererBinding 的_pipelineOwner.rootNode,_pipelineOwner 来自其初始化 initInstances 办法实例化的 PipelineOwner 对象。// 一个 Flutter App 全局只有一个 PipelineOwner 实例。container: renderView, 
      debugShortDescription: '[root]',
      //4、咱们平时写的 dart Widget app
      child: rootWidget,
    //5、attach 过程,buildOwner 来自 WidgetsBinding 初始化时实例化的 BuildOwner 实例,renderViewElement 值就是_renderViewElement 本人,此时因为调用完 appach 才赋值,所以首次进来也是 null。).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
    if (isBootstrapFrame) {//6、首帧被动更新一下,匹配条件的状况下外部实质是调用 SchedulerBinding 的 scheduleFrame()办法。// 进而实质调用了 window.scheduleFrame()办法。SchedulerBinding.instance!.ensureVisualUpdate();}
  }
}

下面代码片段的步骤 2 和步骤 5 须要配合 RenderObjectToWidgetAdapter 类片段查看,如下:

//1、RenderObjectToWidgetAdapter 继承自 RenderObjectWidget,RenderObjectWidget 继承自 Widget
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
  ......
  //3、咱们编写 dart 的 runApp 函数参数中传递的 Flutter 利用 Widget 树根
  final Widget? child;
  //4、继承自 RenderObject,来自 PipelineOwner 对象的 rootNode 属性,一个 Flutter App 全局只有一个 PipelineOwner 实例。final RenderObjectWithChildMixin<T> container;
  ......
  //5、重写 Widget 的 createElement 实现,构建了一个 RenderObjectToWidgetElement 实例,它继承于 Element。//Element 树的根结点是 RenderObjectToWidgetElement。@override
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
  //6、重写 Widget 的 createRenderObject 实现,container 实质是一个 RenderView。//RenderObject 树的根结点是 RenderView。@override
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;

  @override
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }

  /**
   *7、下面代码片段中 RenderObjectToWidgetAdapter 实例创立后调用
   *owner 来自 WidgetsBinding 初始化时实例化的 BuildOwner 实例,element 值就是本人。* 该办法创立根 Element(RenderObjectToWidgetElement),并将 Element 与 Widget 进行关联,即创立 WidgetTree 对应的 ElementTree。* 如果 Element 曾经创立过则将根 Element 中关联的 Widget 设为新的(即_newWidget)。* 能够看见 Element 只会创立一次,前面都是间接复用的。*/
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element]) {
    //8、因为首次实例化 RenderObjectToWidgetAdapter 调用 attachToRenderTree 后才不为 null,所以以后流程为 null
    if (element == null) {
      //9、在 lockState 外面代码执行过程中禁止调用 setState 办法
      owner.lockState(() {
        //10、创立一个 Element 实例,即调用本段代码片段中步骤 5 的办法。// 调用 RenderObjectToWidgetAdapter 的 createElement 办法构建了一个 RenderObjectToWidgetElement 实例,继承 RootRenderObjectElement,又持续继承 RenderObjectElement,接着继承 Element。element = createElement();
        assert(element != null);
        //11、给根 Element 的 owner 属性赋值为 WidgetsBinding 初始化时实例化的 BuildOwner 实例。element!.assignOwner(owner);
      });
      //12、重点!mount 外面 RenderObject 
      owner.buildScope(element!, () {element!.mount(null, null);
      });
    } else {
      //13、更新 widget 树时_newWidget 赋值为新的,而后 element 数根标记为 markNeedsBuild
      element._newWidget = this;
      element.markNeedsBuild();}
    return element!;
  }
  ......
}

对于下面步骤 12 咱们先进去简略看下 Element(RenderObjectToWidgetElement extends RootRenderObjectElement extends RenderObjectElement extends Element)的 mount 办法,重点关注的是父类 RenderObjectElement 中的 mount 办法,如下:

abstract class RenderObjectElement extends Element {
  //1、Element 树通过构造方法 RenderObjectToWidgetElement 持有了 Widget 树实例。(RenderObjectToWidgetAdapter)。@override
  RenderObjectWidget get widget => super.widget as RenderObjectWidget;

  //2、Element 树通过 mount 后持有了 RenderObject 渲染树实例。@override
  RenderObject get renderObject => _renderObject!;
  RenderObject? _renderObject;

  @override
  void mount(Element? parent, Object? newSlot) {
    ......
    //3、通过 widget 树(即 RenderObjectToWidgetAdapter)调用 createRenderObject 办法传入 Element 实例本人获取 RenderObject 渲染树。//RenderObjectToWidgetAdapter.createRenderObject(this)返回的是 RenderObjectToWidgetAdapter 的 container 成员,也就是下面剖析的 RenderView 渲染树根节点。_renderObject = widget.createRenderObject(this);
    ......
  }
}

到这里对于 Flutter 的灵魂“三棵树”来说也能得出如下论断:

  • Widget 树的根结点是 RenderObjectToWidgetAdapter(继承自 RenderObjectWidget extends Widget),咱们 runApp 中传递的 Widget 树就被追加到了这个树根的 child 属性上。
  • Element 树的根结点是 RenderObjectToWidgetElement(继承自 RootRenderObjectElement extends RenderObjectElement extends Element),通过调用 RenderObjectToWidgetAdapter 的 createElement 办法创立,创立 RenderObjectToWidgetElement 的时候把 RenderObjectToWidgetAdapter 通过结构参数传递进去,所以 Element 的 _widget 属性值为 RenderObjectToWidgetAdapter 实例,也就是说 Element 树中 _widget 属性持有了 Widget 树实例。RenderObjectToWidgetAdapter。
  • RenderObject 树的根结点是 RenderView(RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>),在 Element 进行 mount 时通过调用 Widget 树(RenderObjectToWidgetAdapter)的 createRenderObject 办法获取 RenderObjectToWidgetAdapter 结构实例化时传入的 RenderView 渲染树根节点。

下面代码流程对应的时序图大抵如下:

联合上一小结能够很容易看进去三棵树的创立机会(时序图中紫红色节点),也能够很容易看进去 Element 是 Widget 和 RenderObject 之前的一个“桥梁”,其外部持有了两者树根,形象示意如下:

因为篇幅和本文主题起因,咱们重心关注三棵树的诞生流程,对于三棵树之间如何配合进行绘制渲染这里先不开展,前面会专门一篇剖析。

热身帧绘制

到此让咱们先将眼光再回到一开始 runApp 办法的实现中,咱们还差整个办法实现中的最初一个 scheduleWarmUpFrame() 调用,如下:

mixin SchedulerBinding on BindingBase {void scheduleWarmUpFrame() {
    ......
    Timer.run(() {assert(_warmUpFrame);
      handleBeginFrame(null);
    });
    Timer.run(() {assert(_warmUpFrame);
      handleDrawFrame();
      // 重置工夫戳,防止热重载状况从热身帧到热重载帧的时间差,导致隐式动画的跳帧状况。resetEpoch();
      ......
      if (hadScheduledFrame)
        scheduleFrame();});
    // 在此次绘制完结前该办法会锁定事件散发,可保障绘制过程中不会再触发新重绘。// 也就是说在本次绘制完结前不会响应各种事件。lockEvents(() async {
      await endOfFrame;
      Timeline.finishSync();});
  }
}

这段代码的实质这里先不具体开展,因为实质就是渲染帧的提交与触发相干,咱们后边文章会详细分析 framework 层绘制渲染相干逻辑,那时再开展。在这里只用晓得它被调用后会立刻执行一次绘制(不必期待 VSYNC 信号到来)。

这时候仔细的话,你可能会有疑难,后面剖析 attachRootWidget 办法调用时,它的最初一行发现是启动帧则会调用 window.scheduleFrame() 而后等零碎 VSYNC 信号到来触发绘制,既然 VSYNC 信号到来时会触发绘制,这个被动热身帧岂不是能够不要?

是的,不要也是没问题的,只是体验不是很好,会导致初始化卡帧的成果。因为后面 window.scheduleFrame() 发动的绘制申请是在收到零碎 VSYNC 信号后才真正执行,而 Flutter app 初始化时为了尽快出现 UI 而没有期待零碎 VSYNC 信号到来就被动发动一针绘制(也被形象的叫做热身帧),这样最长能够缩小一个 VSYNC 等待时间。

总结

下面就是 Flutter Dart 端三棵树的诞生流程,对于三棵树是如何相互工作的,咱们会在前面专门篇章做剖析,这里就先不开展了。

正文完
 0