阅读 224

Flutter启动runApp经历了啥?

背景

Flutter的第一行程序开始, Dart的main方法中通过调用runApp方法把开发者自己编写的Widget传递进去, 编译运行后, 得到开发者预期的效果。我们很好奇, 这背后经历了什么?或者说, 你经常听到的Flutter三棵树核心机制是什么?接下来, 我们一起探讨。

Flutter程序入口

开发者自己编写的Flutter App一般入口都是在main方法, 其内部通过调用runApp方法将我们整个应用的Widget添加并运行。runApp的方法实现如下:

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}
复制代码

上面三行代码代表Flutter启动的核心三步(级联运算符调用)

  • WidgetsFlutterBinding初始化(ensureInitialized)
  • 调度、绑定根节点, 创建核心三棵树(scheduleAttachRootWidget(app))
  • 调度、绘制热身帧(scheduleWarmUpFrame())

WidgetsFlutterBinding实例及初始化

源码如下:


This is the glue that binds the framework to the Flutter engine.

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

分析源码, 可以发现。这个类上面的一行代码: This is the glue that binds the framework to the Flutter engine. 意思是WidgetsFlutterBinding类是Widget框架和Flutter engine的桥梁。 也是整个Flutter的应用层核心。WidgetsFlutterBinding继承自BindingBase, 并且with了大量的mixin类。通过 ensureInitialized() 方法我们可以得到一个全局单例的 WidgetsFlutterBinding 实例,且 mixin 的一堆 XxxBinding 也被实例化。

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

  • WidgetsFlutterBinding: Widget框架和Flutter engine的桥梁核心桥梁主体,Flutter app 全局唯一。

  • BindingBase: 绑定服务抽象类。

  • GestureBinding: 绑定手势事件。处理屏幕事件分发及事件回调处理, 初始化方法中重点是把事件处理回调_handlePointerDataPacket函数赋值给window的属性, 以便window收到屏幕事件后调用, window实例是Framework层与Engine层处理屏幕事件的桥梁。

    void initInstances() {
      super.initInstances();
      _instance = this;
      window.onPointerDataPacket = _handlePointerDataPacket;
    }
    复制代码
  • SchedulerBinding: Flutter绘制调度器相关绑定类, debug编译模式时统计绘制流程时长等操作。

  • ServicesBinding: Flutter系统平台消息监听绑定类。Platform与Flutter层通信相关服务. 同时注册监听了应用的生命周期。

  • PaintingBinding: Flutter绘制预热缓存等绑定类。重要的是, 绑定了图片缓存。

      void initInstances() {
        super.initInstances();
        _instance = this;
        _imageCache = createImageCache();
        shaderWarmUp?.execute();
      }
    复制代码
  • SemanticsBinding: 语义树和Flutter引擎之间的粘合剂绑定类。

  • RendererBinding: 渲染树和Flutter引擎之间的粘合剂绑定类, 重点是持有了渲染树的根节点。RenderView 就是 RenderObject 渲染树的根节点。

    void initRenderView() {
      assert(!_debugIsRenderViewInitialized);
      assert(() {
        _debugIsRenderViewInitialized = true;
        return true;
      }());
      renderView = RenderView(configuration: createViewConfiguration(), window: window);
      renderView.prepareInitialFrame();
    }
    复制代码
  • WidgetsBinding: Widget树和Flutter引擎之间的粘合剂绑定类。

从上面Flutter的runApp启动方法中, 可以看出, 这些XxxBinding承担的角色大致是一个桥梁关联绑定, 如下:

class_widget_flutter_binding.jpeg

因为我们主要探讨Flutter启动主流程机制, 以及三棵树的诞生流程, 所以我们需要重点关注RendererBinding 和 WidgetsBinding 类的initInstances()方法, 如下:

## RendererBinding类

/// The glue between the render tree and the Flutter engine.

渲染树(render tree) 和 Flutter engine之间的桥梁

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

/// Creates a [RenderView] object to be the root of the
/// [RenderObject] rendering tree, and initializes it so that it
/// will be rendered when the next frame is requested.
///
/// Called automatically when the binding is created.

创建渲染树的根节点对象

void initRenderView() {
  
 ......
 
  /*
  创建渲染树的根节点对象
  */
  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;
}


## WidgetsBinding类

/// The glue between the widgets layer and the Flutter engine.

widgets 和 Flutter engine之间的桥梁

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;

    assert(() {
      _debugAddStackFilters();
      return true;
    }());

    /*
    创建一个管理Widgets的类对象
    BuildOwner类用来跟踪哪些Widget需要重建,并处理用于Widget树的其他任务,例如管理不活跃的Widget等,调试模式触发重建等。
    */
    _buildOwner = BuildOwner();
    
    /*
    回调方法赋值,当第一个可构建元素被标记为脏时调用。
    */
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
    
    /*
    回调方法赋值,当本地配置变化或者AccessibilityFeatures变化时调用。
    */
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    
    ......
  }


复制代码

分析上面的初始化过程, 以及重点关注的RendererBinding、WidgetsBinding初始化方法。我们可以知道RendererBinding 中的 RenderView 就是 RenderObject 渲染树的根节点。我们可以得到以下流程图:

runApp加载流程.png

通过 scheduleAttachRootWidget 创建关联三棵核心树

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

/// The glue between the widgets layer and the Flutter engine.
/// WidgetsBinding类
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {

    /*
        Schedules a [Timer] for attaching the root widget.
        This is called by [runApp] to configure the widget tree. Consider using
        [attachRootWidget] if you want to build the widget tree synchronously.
    */
    @protected
    void scheduleAttachRootWidget(Widget rootWidget) {
          /// 异步执行
          Timer.run(() {
            attachRootWidget(rootWidget);
          });
         }
     }
     
     
    void attachRootWidget(Widget rootWidget) {
       /// 1、 是不是启动帧, 即看renderViewElement是否有赋值
      final bool isBootstrapFrame = renderViewElement == null;
      _readyToProduceFrames = true;
      /// 2、 创建RenderObject、Element、Widget关系树, _renderViewElement值为attachToRenderTree方法返回值
      _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
        /// 3、RenderObjectWithChildMixin类型,继承自RenderObject,RenderObject继承自AbstractNode。
        /// 4、来自RendererBinding的_pipelineOwner.rootNode,_pipelineOwner来自其初始initInstances方法实例化的PipelineOwner对象。
        /// 5、一个Flutter App全局只有一个PipelineOwner实例。
        container: renderView,
        debugShortDescription: '[root]',
        /// 6、外部传进来的Widget, 一般是根导航Widget
        child: rootWidget,
        /// 7、attach过程,buildOwner来自WidgetsBinding初始化时实例化的BuildOwner实例,renderViewElement值就是_renderViewElement自己,此时由于调用完appach才赋值,所以首次进来也是null。
      ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
      
      if (isBootstrapFrame) {
      /// 7、首帧主动更新一下,匹配条件的情况下内部本质是调用SchedulerBinding的scheduleFrame()方法。
      /// 8、进而本质调用了window.scheduleFrame()方法。
        SchedulerBinding.instance!.ensureVisualUpdate();
      }
    }
 }

复制代码

上面的步骤2, 主要是调用RenderObjectToWidgetAdapter类

## RenderObjectToWidgetAdapter类
 
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
  /// Creates a bridge from a [RenderObject] to an [Element] tree.
  ///
  /// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
  /// 1、创建一个桥梁关联RenderObject、Element
  RenderObjectToWidgetAdapter({
    this.child,
    required this.container,
    this.debugShortDescription,
  }) : super(key: GlobalObjectKey(container));

  /// 2、我们编写dart的runApp函数参数中传递的Flutter应用Widget树根, 比如MyApp
  final Widget? child;

  /// 3、继承自RenderObject,来自PipelineOwner对象的rootNode属性,一个Flutter App全局只有一个PipelineOwner实例。`
  final RenderObjectWithChildMixin<T> container;

  /// A short description of this widget used by debugging aids.
  final String? debugShortDescription;

  /// 4、重写Widget的createElement实现,构建了一个RenderObjectToWidgetElement实例,它继承于Element。Element树的根结点是RenderObjectToWidgetElement。
  @override
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

  /// 5、重写Widget的createRenderObject实现,container本质是一个RenderView。RenderObject树的根结点是RenderView。
  @override
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;

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

  /// Inflate this widget and actually set the resulting [RenderObject] as the
  /// child of [container].
  ///
  /// If `element` is null, this function will create a new element. Otherwise,
  /// the given element will have an update scheduled to switch to this widget.
  ///
  /// Used by [runApp] to bootstrap applications.
     /* 
     6、上面代码片段中RenderObjectToWidgetAdapter实例创建后调用
        owner来自WidgetsBinding初始化时实例化的BuildOwner实例,element 值就是自己。
        该方法创建根Element(RenderObjectToWidgetElement),并将Element与Widget进行关联,即创建   WidgetTree对应的ElementTree。
        如果Element已经创建过则将根Element中关联的Widget设为新的(即_newWidget)。
        可以看见Element只会创建一次,后面都是直接复用的。
    */
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
      /// 7、由于首次实例化RenderObjectToWidgetAdapter调用attachToRenderTree后才不为null,所以当前流程为null
    if (element == null) {
     /// 8、在lockState里面代码执行过程中禁止调用setState方法
      owner.lockState(() {
      /* 
        9、创建一个Element实例,即调用本段代码片段中步骤4的方法
        调用RenderObjectToWidgetAdapter的createElement方法构建了一个RenderObjectToWidgetElement实例,继承RootRenderObjectElement,又继续继承RenderObjectElement,接着继承Element。  
      */
        element = createElement();
        assert(element != null);
        /// 10、给根Element的owner属性赋值为WidgetsBinding初始化时实例化的BuildOwner实例。
        element!.assignOwner(owner);
      });
      /// 11、重点!mount里面RenderObject, 执行build过程
      owner.buildScope(element!, () {
        element!.mount(null, null);
      });
    } else {
     /// 12、更新widget树时_newWidget赋值为新的,然后element数根标记为markNeedsBuild
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element!;
  }

  @override
  String toStringShort() => debugShortDescription ?? super.toStringShort();
}
复制代码

上面步骤2接收的外部传进来的widget, 特别重要。因为创建RenderObjectToWidgetElement根节点后, 就会调用RenderObjectToWidgetElement根节点的mount方法, 去生成传进来的widget的element了。具体源码, 我们继续分析。

上面步骤11owner.buildScope(element!, ())执行build过程, 我们跳进源码看下。

void buildScope(Element context, [ VoidCallback? callback ]) {
  if (callback == null && _dirtyElements.isEmpty)
    return;
    
  ......
  
  Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent);
  try {
    _scheduledFlushDirtyElements = true;
    if (callback != null) {
      assert(_debugStateLocked);
      Element? debugPreviousBuildTarget;
      assert(() {
        context._debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
        debugPreviousBuildTarget = _debugCurrentBuildTarget;
        _debugCurrentBuildTarget = context;
        return true;
      }());
      _dirtyElementsNeedsResorting = false;
      try {
        callback();
      } finally {
        ......
      }
    }
    _dirtyElements.sort(Element._sort);
    _dirtyElementsNeedsResorting = false;
    int dirtyCount = _dirtyElements.length;
    int index = 0;
    while (index < dirtyCount) {
      /// 针对脏元素执行rebuild操作
        _dirtyElements[index].rebuild();
    } 
  
  } finally {
    
   ......
   
    Timeline.finishSync();
  }
  assert(_debugtateLockLevel >= 0);
}
复制代码

上面步骤11element!.mount(null, null), 我们跳进源码看下。RenderObjectToWidgetElement extends RootRenderObjectElement extends RenderObjectElement extends Element。重点关注的是element的根节点RenderObjectToWidgetElement, 和父类 RenderObjectElement 中的 mount 方法,如下:

RenderObjectToWidgetElement

element根节点RenderObjectToWidgetElement源码如下:

class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
  /// Creates an element that is hosted by a [RenderObject].
  ///
  /// The [RenderObject] created by this element is not automatically set as a
  /// child of the hosting [RenderObject]. To actually attach this element to
  /// the render tree, call [RenderObjectToWidgetAdapter.attachToRenderTree].
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);

  @override
  RenderObjectToWidgetAdapter<T> get widget => super.widget as RenderObjectToWidgetAdapter<T>;

 /// 1、这个 私有_child特别重要, 它就是外部传进来的widget(MyApp)对象的elment。
  Element? _child;

  static const Object _rootChildSlot = Object();

  @override
  void visitChildren(ElementVisitor visitor) {
    if (_child != null)
      visitor(_child!);
  }

  @override
  void forgetChild(Element child) {
    assert(child == _child);
    _child = null;
    super.forgetChild(child);
  }

  @override
  void mount(Element? parent, Object? newSlot) {
    assert(parent == null);
    super.mount(parent, newSlot);
    _rebuild();
    assert(_child != null);
  }

  @override
  void update(RenderObjectToWidgetAdapter<T> newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _rebuild();
  }

  // When we are assigned a new widget, we store it here
  // until we are ready to update to it.
  Widget? _newWidget;

  @override
  void performRebuild() {
    if (_newWidget != null) {
      // _newWidget can be null if, for instance, we were rebuilt
      // due to a reassemble.
      final Widget newWidget = _newWidget!;
      _newWidget = null;
      update(newWidget as RenderObjectToWidgetAdapter<T>);
    }
    super.performRebuild();
    assert(_newWidget == null);
  }

  @pragma('vm:notify-debugger-on-exception')
  void _rebuild() {
    try {
     /// 2、更新操作, 特别重要。 这个_child就是外部传进来的widget(MyApp)对象的elment。
     /// 这个方法由基类Elemen提供并实现。
      _child = updateChild(_child, widget.child, _rootChildSlot);
    } catch (exception, stack) {
      final FlutterErrorDetails details = FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'widgets library',
        context: ErrorDescription('attaching to the render tree'),
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
      _child = updateChild(null, error, _rootChildSlot);
    }
  }

  @override
  RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>;

  @override
  void insertRenderObjectChild(RenderObject child, Object? slot) {
    assert(slot == _rootChildSlot);
    assert(renderObject.debugValidateChild(child));
    renderObject.child = child as T;
  }

  @override
  void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
    assert(false);
  }

  @override
  void removeRenderObjectChild(RenderObject child, Object? slot) {
    assert(renderObject.child == child);
    renderObject.child = null;
  }
}
复制代码

RenderObjectElement

RenderObjectElement源码如下:

abstract class RenderObjectElement extends Element {
  /// Creates an element that uses the given widget as its configuration.
  RenderObjectElement(RenderObjectWidget widget) : super(widget);

  /// 1、Element树通过构造方法RenderObjectToWidgetElement持有了Widget树实例。(RenderObjectToWidgetAdapter)。
  @override
  RenderObjectWidget get widget => super.widget as RenderObjectWidget;
  
  /// 2、Element树通过mount后持有了RenderObject渲染树实例。
  @override
  RenderObject get renderObject {
    assert(_renderObject != null, '$runtimeType unmounted');
   return _renderObject!;
  }
  RenderObject? _renderObject;

  @override
  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    assert(() {
      _debugDoingBuild = true;
      return true;
    }());
    /// 3、通过widget树(即RenderObjectToWidgetAdapter)调用createRenderObject方法传入Element实例自己获取RenderObject渲染树。RenderObjectToWidgetAdapter.createRenderObject(this)返回的是RenderObjectToWidgetAdapter的container成员,也就是上面分析的RenderView渲染树根节点。
    _renderObject = widget.createRenderObject(this);
    assert(!_renderObject!.debugDisposed!);
    assert(() {
      _debugDoingBuild = false;
      return true;
    }());
    assert(() {
      _debugUpdateRenderObjectOwner();
      return true;
    }());
    assert(_slot == newSlot);
    attachRenderObject(newSlot);
    _dirty = false;
  }

}

复制代码

Element

因为RenderObjectToWidgetElement根节点调用mount时,执行了Element类中的updateChild方法, 所以, 我们重点关注Element的updateChild、inflateWidget方法.

abstract class Element extends DiagnosticableTree implements BuildContext {
  /// Creates an element that uses the given widget as its configuration.
  ///
  /// Typically called by an override of [Widget.createElement].
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

  Element? _parent;
  DebugReassembleConfig? _debugReassembleConfig;

  /// The configuration for this element.
  @override
  Widget get widget => _widget!;
  Widget? _widget;
  
  /// Update the given child with the given new configuration.
  ///
  /// This method is the core of the widgets system. It is called each time we
  /// are to add, update, or remove a child based on an updated configuration.
  ///
  /// The `newSlot` argument specifies the new value for this element's [slot].
  ///
  /// If the `child` is null, and the `newWidget` is not null, then we have a new
  /// child for which we need to create an [Element], configured with `newWidget`.
  ///
  /// If the `newWidget` is null, and the `child` is not null, then we need to
  /// remove it because it no longer has a configuration.
  ///
  /// If neither are null, then we need to update the `child`'s configuration to
  /// be the new configuration given by `newWidget`. If `newWidget` can be given
  /// to the existing child (as determined by [Widget.canUpdate]), then it is so
  /// given. Otherwise, the old child needs to be disposed and a new child
  /// created for the new configuration.
  ///
  /// If both are null, then we don't have a child and won't have a child, so we
  /// do nothing.
  ///
  /// The [updateChild] method returns the new child, if it had to create one,
  /// or the child that was passed in, if it just had to update the child, or
  /// null, if it removed the child and did not replace it.
  ///
  /// The following table summarizes the above:
  ///
  /// |                     | **newWidget == null**  | **newWidget != null**   |
  /// | :-----------------: | :--------------------- | :---------------------- |
  /// |  **child == null**  |  Returns null.         |  Returns new [Element]. |
  /// |  **child != null**  |  Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
  ///
  /// The `newSlot` argument is used only if `newWidget` is not null. If `child`
  /// is null (or if the old child cannot be updated), then the `newSlot` is
  /// given to the new [Element] that is created for the child, via
  /// [inflateWidget]. If `child` is not null (and the old child _can_ be
  /// updated), then the `newSlot` is given to [updateSlotForChild] to update
  /// its slot, in case it has moved around since it was last built.
  ///
  /// See the [RenderObjectElement] documentation for more information on slots.
  @protected
  @pragma('vm:prefer-inline')
  Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
    if (newWidget == null) {
      if (child != null)
        deactivateChild(child);
      return null;
    }
    final Element newChild;
    if (child != null) {
      bool hasSameSuperclass = true;
      // When the type of a widget is changed between Stateful and Stateless via
      // hot reload, the element tree will end up in a partially invalid state.
      // That is, if the widget was a StatefulWidget and is now a StatelessWidget,
      // then the element tree currently contains a StatefulElement that is incorrectly
      // referencing a StatelessWidget (and likewise with StatelessElement).
      //
      // To avoid crashing due to type errors, we need to gently guide the invalid
      // element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
      // returns false which prevents us from trying to update the existing element
      // incorrectly.
      //
      // For the case where the widget becomes Stateful, we also need to avoid
      // accessing `StatelessElement.widget` as the cast on the getter will
      // cause a type error to be thrown. Here we avoid that by short-circuiting
      // the `Widget.canUpdate` check once `hasSameSuperclass` is false.
      assert(() {
        final int oldElementClass = Element._debugConcreteSubtype(child);
        final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
        hasSameSuperclass = oldElementClass == newWidgetClass;
        return true;
      }());
      if (hasSameSuperclass && child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        newChild = child;
      } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        child.update(newWidget);
        assert(child.widget == newWidget);
        assert(() {
          child.owner!._debugElementWasRebuilt(child);
          return true;
        }());
        newChild = child;
      } else {
        deactivateChild(child);
        assert(child._parent == null);
        newChild = inflateWidget(newWidget, newSlot);
      }
    } else {
     
     /// 惊天秘密!!! 首次进入的时候, newChild为空。这里调用了inflateWidget方法, 根据外部传进来的widget(Myapp), 创建widget的elment节点并赋值给_child, 然后返回给根RenderObjectToWidgetElement持有。
      newChild = inflateWidget(newWidget, newSlot);
    }

    ....
    
    return newChild;
  }

  /// Add this element to the tree in the given slot of the given parent.
  ///
  /// The framework calls this function when a newly created element is added to
  /// the tree for the first time. Use this method to initialize state that
  /// depends on having a parent. State that is independent of the parent can
  /// more easily be initialized in the constructor.
  ///
  /// This method transitions the element from the "initial" lifecycle state to
  /// the "active" lifecycle state.
  ///
  /// Subclasses that override this method are likely to want to also override
  /// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
  /// [RenderObjectElement.moveRenderObjectChild], and
  /// [RenderObjectElement.removeRenderObjectChild].
  ///
  /// Implementations of this method should start with a call to the inherited
  /// method, as in `super.mount(parent, newSlot)`.
  @mustCallSuper
  void mount(Element? parent, Object? newSlot) {
    assert(_lifecycleState == _ElementLifecycle.initial);
    assert(widget != null);
    assert(_parent == null);
    assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
    assert(slot == null);
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null) {
      // Only assign ownership if the parent is non-null. If parent is null
      // (the root node), the owner should have already been assigned.
      // See RootRenderObjectElement.assignOwner().
      _owner = parent.owner;
    }
    assert(owner != null);
    final Key? key = widget.key;
    if (key is GlobalKey) {
      owner!._registerGlobalKey(key, this);
    }
    _updateInheritance();
  }

   ......
   
  /// Create an element for the given widget and add it as a child of this
  /// element in the given slot.
  ///
  /// This method is typically called by [updateChild] but can be called
  /// directly by subclasses that need finer-grained control over creating
  /// elements.
  ///
  /// If the given widget has a global key and an element already exists that
  /// has a widget with that global key, this function will reuse that element
  /// (potentially grafting it from another location in the tree or reactivating
  /// it from the list of inactive elements) rather than creating a new element.
  ///
  /// The `newSlot` argument specifies the new value for this element's [slot].
  ///
  /// The element returned by this function will already have been mounted and
  /// will be in the "active" lifecycle state.
  @protected
  @pragma('vm:prefer-inline')
  Element inflateWidget(Widget newWidget, Object? newSlot) {
    assert(newWidget != null);
    final Key? key = newWidget.key;
    if (key is GlobalKey) {
      final Element? newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        assert(newChild._parent == null);
        assert(() {
          _debugCheckForCycles(newChild);
          return true;
        }());
        newChild._activateWithParent(this, newSlot);
        final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
        assert(newChild == updatedChild);
        return updatedChild!;
      }
    }
    /// 惊天秘密!!! 拿到外部传进来的widget(MyApp), 创建widget(MyApp)的element, 并返回。
    final Element newChild = newWidget.createElement();
    /// 惊天秘密!!! 生成newChild后, 立即执行elment的mount方法, 挂载节点。
    newChild.mount(this, newSlot);
    assert(newChild._lifecycleState == _ElementLifecycle.active);
    return newChild;
  }
}
复制代码

从上面的源码分析来看, 我们可以得到以下结论:

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

上面源码流程对应的时序图如下:

Flutter启动流程分析.png

现在我们回到最初的runApp方法的实现中, 探讨下热身帧scheduleWarmUpFrame

scheduleWarmUpFrame热身帧


void scheduleWarmUpFrame() {
  if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
    return;

  _warmUpFrame = true;
  Timeline.startSync('Warm-up frame');
  final bool hadScheduledFrame = _hasScheduledFrame;
  // We use timers here to ensure that microtasks flush in between.
  Timer.run(() {
    assert(_warmUpFrame);
    handleBeginFrame(null);
  });
  Timer.run(() {
    assert(_warmUpFrame);
    handleDrawFrame();
    // We call resetEpoch after this frame so that, in the hot reload case,
    // the very next frame pretends to have occurred immediately after this
    // warm-up frame. The warm-up frame's timestamp will typically be far in
    // the past (the time of the last real frame), so if we didn't reset the
    // epoch we would see a sudden jump from the old time in the warm-up frame
    // to the new time in the "real" frame. The biggest problem with this is
    // that implicit animations end up being triggered at the old time and
    // then skipping every frame and finishing in the new time.
    /// 重置时间戳,避免热重载情况从热身帧到热重载帧的时间差,导致隐式动画的跳帧情况。
    resetEpoch();
    _warmUpFrame = false;
    if (hadScheduledFrame)
      scheduleFrame();
  });

  // Lock events so touch events etc don't insert themselves until the
  // scheduled frame has finished.
  /// 在此次绘制结束前该方法会锁定事件分发,可保证绘制过程中不会再触发新重绘。
  /// 也就是说在本次绘制结束前不会响应各种事件。
  lockEvents(() async {
    await endOfFrame;
    Timeline.finishSync();
  });
}

复制代码

进入布局(Layout)阶段和绘制(Paint)阶段。

这里其实onBeginFrame和onDrawFrame是在具体执行这两个回调。最后渲染出来首帧场景送入engine显示到屏幕。这里使用Timer.run()来异步运行两个回调,是为了在它们被调用之前有机会处理完微任务队列(microtask queue)

总结

WidgetsFlutterBinding是单例模式。

WidgetsBinding的renderViewElement记录着唯一的RenderObjectToWidgetElement对象。 RendererBinding的renderView记录着唯一的RenderView对象。 也就是说每个flutter应用创建的Root Widget跟Element、RenderObject一一对应,且单例唯一。

MyApp是用户定义的根Widget,为了建立三棵树的关系,RenderObjectToWidgetAdapter起到重要的桥接功能,该类的createElement方法创建RenderObjectToWidgetElement对象,createRenderObject()方法获取的是RenderView。

上面就是 Flutter 启动runApp的经历与三棵树的关系, 至于三棵树是如何协同工作的, 我们下一篇再讲。

参考资料

Flutter的Widget渲染核心

Flutter中Widget的生命周期和渲染原理_RunningKing-程序员ITS304

深入理解Flutter应用启动

Flutter渲染之Widget、Element 和 RenderObject

文章分类
前端
文章标签