Flutter-runApp()方法

3,282 阅读5分钟

Flutter程序的入口main()方法会调用runApp()方法,我们本篇探索runApp都做了啥。

概览

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

这个方法一看很简洁,一共调用了WidgetsFlutterBinding的三个方法,在看三个方法的实现之前,我们看一下WidgetsFlutterBinding是什么。

WidgetsFlutterBinding

我们看一下官方的解释:

A concrete binding for applications based on the Widgets framework.This is the glue that binds the framework to the Flutter engine.

翻译过来就是,一个基于Widgets framework的应用程序的具体绑定,它是绑定frameworkFlutter engine的胶水层。

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

它的父类BindingBase是一个抽象类,with实现了很多mixin,这些mixin只能用于继承自BindingBase的类。mixin的作用是扩展功能,mixin可以类比于iOSprotocol(个人见解,如果不对欢迎指正)。

BindingBase

abstract class BindingBase {
  /// 省略代码
  /// However, multiple window support is not yet implemented, so currently this
  /// provides access to the one and only window.
  // TODO(gspencergoog): remove the preceding note once multi-window support is
  // active.
  // 唯一的window
  ui.SingletonFlutterWindow get window => ui.window;
  /// 每一个BindingBase类定义行为 都有一个 platformDispatcher 作为回调(handlers)
  ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
  /// 初始化实例
  void initInstances() {
    assert(!_debugInitialized);
    assert(() {
      _debugInitialized = true;
      return true;
    }());
  }
  /// The current [WidgetsBinding], if one has been created.
  /// ensureInitialized方法返回的实例
  /// If you need the binding to be constructed before calling [runApp],
  /// you can ensure a Widget binding has been constructed by calling the
  /// `WidgetsFlutterBinding.ensureInitialized()` function.
  static WidgetsBinding? get instance => _instance;
  static WidgetsBinding? _instance;
  /// 注册 service extensions 初始化之后调用
  void initServiceExtensions() {
    ///省略代码
  }
}
  • ui.window:是Flutter App显示的窗口,它继承自FlutterView,位于Flutter engine层。
  • ui.PlatformDispatcher.instance:platformDispatcher是Flutter 的一个事件分发器,负责Flutter分发engine的事件,和传递事件给engine层。
  • initInstances:初始化实例的方法。
  • initServiceExtensions():注册 service extensions,比如platformOverrideactiveDevToolsServerAddress等。

ensureInitialized()方法

该方法的作用是返回一个WidgetsBinding类型实例,如果未创建就新创建一个。

static WidgetsBinding ensureInitialized() {
  if (WidgetsBinding.instance == null)
    WidgetsFlutterBinding();
  return WidgetsBinding.instance!;
}

就是返回一个WidgetsBinding.instance实例,因为WidgetsFlutterBinding实现了GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBindingmixin,mixin的initInstancesinitServiceExtensions方法也会调用,每个mixin的功能:

  • GestureBinding:处理手势。
  • SchedulerBinding: 处理系统调度。
  • ServicesBinding:处理与原生的交互。
  • PaintingBinding:处理绘制。
  • SemanticsBinding:处理语义化。
  • RendererBinding:处理渲染。
  • WidgetsBindingWidgets相关。

我们下面主要看WidgetsBindingRendererBinding

WidgetsBinding

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    assert(() {
      _debugAddStackFilters();
      return true;
    }());
    // Initialization of [_buildOwner] has to be done after
    // [super.initInstances] is called, as it requires [ServicesBinding] to
    // properly setup the [defaultBinaryMessenger] instance.
    _buildOwner = BuildOwner();
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
 }
  ///省略代码
}

WidgetsBinding初始化会创建一个BuildOwner对象,它的作用是管理Widget树和Element树。

RendererBinding

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }
​
  /// The current [RendererBinding], if one has been created.
  static RendererBinding? get instance => _instance;
  static RendererBinding? _instance;
  ///省略代码
}

RendererBinding初始化会创建一个PipelineOwner对象,用于管理RenderObject树。PipelineOwnerBuildOwner都位于framework层,它们通过Bingding(胶水层)与engine交互。

  • 初始化了一个PipelineOwner用于管理RenderObject.
  • _handlePersistentFrameCallback这个callback传入SchedulerBinding中的_postFrameCallbacks中,这样在硬件每次发出VSync信号的时候都会调用RenderBinding中的_handlePersistentFrameCallback方法._handlePersistentFrameCallback方法中直接调用了drawFrame方法。

scheduleAttachRootWidget

实例化之后会调用scheduleAttachRootWidget方法。

  @protected
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }

调用了attachRootWidget方法:

  void attachRootWidget(Widget rootWidget) {
    final bool isBootstrapFrame = renderViewElement == null;
    _readyToProduceFrames = true;
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
    if (isBootstrapFrame) {
      SchedulerBinding.instance!.ensureVisualUpdate();
    }
  }

attachRootWidget方法用于是为根Widget生成一个根Element。生成Element调用了attachToRenderTree方法并传入了BuildOwner和Element。

RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
    if (element == null) {
      ///新创建一个 element
      owner.lockState(() {
        element = createElement();
        assert(element != null);
        element!.assignOwner(owner);
      });
      // 建立能更新widget 树的能力,可以回调 callback,构建所有标记为dirty的elment
      owner.buildScope(element!, () {
        element!.mount(null, null);
      });
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element!;
 }

这个方法主要是element为空的时候新建一个element,新建后会调用BuildOwnerbuildScope主要作用是建立能更新widget 树的能力,可以回调 callback,构建所有标记为dirtyelement

attachRootWidget方法,最后会执行SchedulerBinding.instance!.ensureVisualUpdate()

  void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

它主要是调用新的帧的调度管理。它会调用scheduleFrame方法

  void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled)
      return;
    assert(() {
      if (debugPrintScheduleFrameStacks)
        debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
      return true;
    }());
    ///给window设置回调
    ensureFrameCallbacksRegistered();
    ///调度更新
    window.scheduleFrame();
    _hasScheduledFrame = true;
  }

这里会给window设置onBeginFrameonDrawFrame的回调,window会把回调传给platformDispatcher

  @override
  set onBeginFrame(ui.FrameCallback? callback) {
    platformDispatcher.onBeginFrame = callback;
  }
  @override
  ui.VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame;
  @override
  set onDrawFrame(ui.VoidCallback? callback) {
    platformDispatcher.onDrawFrame = callback;
  }

也就是说scheduleAttachRootWidget经过一系列调用之后,会把SchedulerBinding_handleBeginFrame_handleDrawFrame传给platformDispatcherplatformDispatcher分发来自enginee的事件。而在这里SingletonFlutterWindowplatformDispatcheronBeginFrameonDrawFrame这两个事件交给SchedulerBinding处理。

当硬件发出VSync信号时,会调用platformDispatcher的onDrawFrame。实际上会调用SchedulerBinding中的_handleDrawFrame方法。_handleDrawFrame会调用handleDrawFrame方法:

  void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    Timeline.finishSync(); // end the "Animate" phase
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (final FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
      // POST-FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (final FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
    } finally {
      ///省略代码
    }
  }

_postFrameCallbacks里面存储的是callback,作用是硬件每次发出VSync信号的时候都会调用。这里的_postFrameCallbacks是在RenderBinding这个mixininitInstances方法中传入的

addPersistentFrameCallback(_handlePersistentFrameCallback);

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();
    });
  }

这个方法主要调用是scheduleFrame,跟进代码实际是调用的window.scheduleFrame(),

  @override
  void scheduleFrame() {
    platformDispatcher.scheduleFrame();
  }

window.scheduleFrame()调用了platformDispatcher.scheduleFrame(),通知engine层需要绘制。engine会根据情况尽快地调用platformDispatcher的onDrawFrame方法。

总结

runApp方法主要做了以下事情:

  • 创建WidgetsFlutterBinding它是连接frameworkengine的胶水层。注册Vsync回调,后面每一帧的调用会出发WidgetsFlutterBinding的回调,最后传递到framework层处理逻辑。
  • attachRootWidget:遍历挂载整个视图树,建立widgetelementrenderObjcect的连接关系。
  • scheduleWarmUpFrame:调度帧预热(warmUp)。执行帧绘制handleBeginFramehandleDrawFrame方法。