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继承自Widgetclass 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 端三棵树的诞生流程,对于三棵树是如何相互工作的,咱们会在前面专门篇章做剖析,这里就先不开展了。