Flutter框架分析 -- runApp初始化

587 阅读8分钟

这篇文章里,我们从Flutter框架的初始化来进入,来一步步揭开Flutter的面纱。写过Flutter程序的同学都知道,Flutter app的入口就是函数runApp()

void main() {
  runApp(MyApp(());
}

那么我们就从函数runApp()入手,看看这个函数被调用以后发生了什么

初始化


runApp()的函数体位于widgets/bindding.dart.


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

从调用的函数名称可以看出来,它做了3件事,

  • 确保WidgetsFlutterBinding被初始化.
  • 计划附加根Widget, 就是把传入的app添加到哪里去.
  • 计划预热框架,调度一个“热身”帧.

下面挨个来看这三件事具体都做了什么.

ensureInitialized

首先我们看一下WidgetsFlutterBinding是什么,从这个类名称来看,就是Widget和Flutter进行绑定的意思.

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 并且混入MixinGestureBindingSchedulerBindingServicesBindingSemanticsBindingRendererBindingWidgetsBinding 混入的这几个类也都是继承自BindingBase.

静态函数ensureInitiallized()所做的就是返回一个WidgetsBinding.instance单例.

abstract class BindingBase {
  BindingBase() {
    initInstances();
  }

/// A number of additional bindings are defined as extensions of
/// [BindingBase], e.g., [ServicesBinding], [RendererBinding], and
/// [WidgetsBinding]. Each of these bindings define behaviors that interact
/// with a [ui.PlatformDispatcher], e.g., [ServicesBinding] registers
/// listeners with the [ChannelBuffers], and [RendererBinding]
/// registers [ui.PlatformDispatcher.onMetricsChanged],
/// [ui.PlatformDispatcher.onTextScaleFactorChanged],
/// [ui.PlatformDispatcher.onSemanticsEnabledChanged], and
/// [ui.PlatformDispatcher.onSemanticsAction] handlers.
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance; 

关于抽象类BindingBase,需要了解两个地方,一个是在其构造的时候会调用函数MixinxxBinding绑定类各自实现.

BindingBase有一个getter, 返回的PlatformDispatcherBindingBse主要就是对PlatformDispatcher的封装.

接着来看这几个绑定类都做了什么吧.

GestureBinding, 手势绑定

mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    platformDispatcher.onPointerDataPacket = _handlePointerDataPacket;
  }

在调用initInstaces()的时候,主要做的事情就是给platformDispatcher设置一个手势处理的回调函数.这个类主要是负责处理手势事件的.

SchedulerBinding, 调度绑定

mixin SchedulerBinding on BindingBase {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
  }

/// Ensures callbacks for [PlatformDispatcher.onBeginFrame] and
/// [PlatformDispatcher.onDrawFrame] are registered.
@protected
void ensureFrameCallbacksRegistered() {
  platformDispatcher.onBeginFrame ??= _handleBeginFrame;
  platformDispatcher.onDrawFrame ??= _handleDrawFrame;
}

这个绑定主要是给platformDispatcher设置了onBeginFrameonDrawFrame的回调,在讲渲染流水线的时候, 当Vsync信号到来的时候,engine会回调这两个函数来启动渲染流程, SchedulerBinding就是用来管理这两个回调的.

ServicesBinding, 服务绑定

mixin ServicesBinding on BindingBase, SchedulerBinding {

    @override
    void initInstances() {
      super.initInstances();
      _instance = this;
      _defaultBinaryMessenger = createBinaryMessenger();
      _restorationManager = createRestorationManager();
      _initKeyboard();
      initLicenses();
      SystemChannels.system.setMessageHandler((dynamic message) => handleSystemMessage(message as Object));
      SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
      SystemChannels.platform.setMethodCallHandler(_handlePlatformMessage);
      TextInput.ensureInitialized();
      readInitialLifecycleStateFromNativeWindow();
    }

上面可以看到这个类做的操作还是很多的,有initXX各类初始化方法,setMessageHandler各类消息处理, 下面我们来逐个分析几个主要的方法.

_initKeyboard
void _initKeyboard() {
  _keyboard = HardwareKeyboard();
  _keyEventManager = KeyEventManager(_keyboard, RawKeyboard.instance);
  platformDispatcher.onKeyData = _keyEventManager.handleKeyData;
  SystemChannels.keyEvent.setMessageHandler(_keyEventManager.handleRawKeyMessage);
}

可以看到这个类做的操作很简单,初始化了HardwareKeyboard键盘类, KeyEventManager键入事件管理器,设置了onKeyData键入数据回调,keyEvent键入事件回调,可以看的出来这里就是处理键盘输入的地方

TextInput.ensureInitialized

TextInput.ensureInitialized();

class TextInput {
  TextInput._() {
    _channel = SystemChannels.textInput;
    _channel.setMethodCallHandler(_loudlyHandleTextInputInvocation);
  }
  
  /// Ensure that a [TextInput] instance has been set up so that the platform
  /// can handle messages on the text input method channel.
  static void ensureInitialized() {
    _instance; // ignore: unnecessary_statements
  }
  static final TextInput _instance = TextInput._();
  
}

可以看到这里的处理也非常简单,初始化TextInput用来处理系统文本输入消息

readInitialLifecyleStateFromNativeWindow
/// The latest state should be obtained by subscribing to
/// [WidgetsBindingObserver.didChangeAppLifecycleState].
@protected
void readInitialLifecycleStateFromNativeWindow() {
  if (lifecycleState != null) {
    return;
  }
  final AppLifecycleState? state = _parseAppLifecycleMessage(platformDispatcher.initialLifecycleState);
  if (state != null) {
    handleAppLifecycleStateChanged(state);
  }
}

static AppLifecycleState? _parseAppLifecycleMessage(String message) {
  switch (message) {
    case 'AppLifecycleState.paused':
      return AppLifecycleState.paused;
    case 'AppLifecycleState.resumed':
      return AppLifecycleState.resumed;
    case 'AppLifecycleState.inactive':
      return AppLifecycleState.inactive;
    case 'AppLifecycleState.detached':
      return AppLifecycleState.detached;
  }
  return null;
}

mixin SchedulerBinding on BindingBase {

  @protected
  @mustCallSuper
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
    assert(state != null);
    _lifecycleState = state;
    switch (state) {
      case AppLifecycleState.resumed:
      case AppLifecycleState.inactive:
        _setFramesEnabledState(true);
        break;
      case AppLifecycleState.paused:
      case AppLifecycleState.detached:
        _setFramesEnabledState(false);
        break;
    }
  }

  bool _framesEnabled = true;
  void _setFramesEnabledState(bool enabled) {
    if (_framesEnabled == enabled) {
      return;
    }
    _framesEnabled = enabled;
    if (enabled) {
      scheduleFrame();
    }
  }
}

readInitinalLifecryleStateFromNaviteWindow做的东西也非常简单,获取platformDispatcher.initialLifecrycleState获取平台初始化生命周期状态,交给SchedulerBinding这个类的handleAppLifecrycleStateChanged来处理,也就是设置框架状态,如果是true则会调用框架执行一帧.

SystemChannels

可以看到这个类出现的镜头还是非常多的,系统通道通过名字我们就可以知道,这个类提供了非常多的和系统操作相关的Channel

/// Platform channels used by the Flutter system.
class SystemChannels {
  // This class is not meant to be instantiated or extended; this constructor
  // prevents instantiation and extension.
  SystemChannels._();
}

///  * `memoryPressure`: Indicates that the operating system would like
///    applications to release caches to free up more memory. See
///    [WidgetsBindingObserver.didHaveMemoryPressure], which triggers whenever
///    a message is received on this channel.
static const BasicMessageChannel<Object?> system = BasicMessageChannel<Object?>(
    'flutter/system',
    JSONMessageCodec(),
);

///  * [WidgetsBindingObserver.didChangeAppLifecycleState], which triggers
///    whenever a message is received on this channel.
static const BasicMessageChannel<String?> lifecycle = BasicMessageChannel<String?>(
    'flutter/lifecycle',
    StringCodec(),
);

/// Calls to methods that are not implemented on the shell side are ignored
/// (so it is safe to call methods when the relevant plugin might be missing).
static const MethodChannel platform = OptionalMethodChannel(
    'flutter/platform',
    JSONMethodCodec(),
);

SystemChannels提供的Channel还是非常的多,这里只列出了ServicesBinding处理了消息回调的三种,感兴趣的可以自己去看一下. 到这里SetvicesBinding做的主要操作就分析完了.

PaintingBinding, 绘制绑定

/// Binding for the painting library.
///
/// Hooks into the cache eviction logic to clear the image cache.
///
/// Requires the [ServicesBinding] to be mixed in earlier.
mixin PaintingBinding on BindingBase, ServicesBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _imageCache = createImageCache();
    shaderWarmUp?.execute();
  }
}

这个类创建了createIamgeCache图片缓存, 执行了图像绘制的”预热“, 这里就不往下看了.

SamanticBinding, 辅助功能绑定

mixin SemanticsBinding on BindingBase {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _accessibilityFeatures = platformDispatcher.accessibilityFeatures;
  }
}

/// Additional accessibility features that may be enabled by the platform.
AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures;

可以看下官方对accessibilityFeatures的描述,平台可能启用的其他辅助功能. 而SeamanticsBinding就是对辅助型功能的一个管理.

RenderBinding, 渲染绑定.

The glue between the render tree and the Flutter engine. 渲染树和Flutter引擎之间的粘合, 这个类是用来连接渲染树和Flutter引擎的,是比较重要的一个类

/// The glue between the render tree and the Flutter engine.
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsUpdate: _handleSemanticsUpdate,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    platformDispatcher
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }
}

RendererBinding绑定类初始化的时候,做的事情还是非常多的,渲染流程类PipelineOwner就是在这里创建并进行管理的,这个类就是我们说的渲染流水线.

绑定了platformDispatcher的一系列回调文本比例屏幕尺寸亮度变化等回调.

/// 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() {
  assert(!_debugIsRenderViewInitialized);
  assert(() {
    _debugIsRenderViewInitialized = true;
    return true;
  }());
  renderView = RenderView(configuration: createViewConfiguration(), window: window);
  renderView.prepareInitialFrame();
}

initRenderView创建了一个RenderView类. RenderView继承自RenderObject 官方对RenderView的描述是: 创建[RenderView]对象作为[RenderObject]渲染树,并对其进行初始化,使其将在请求下一帧时呈现。创建绑定时自动调用。

我们都知道Flutter渲染后是存在一个渲染树Render tree的,这里的RenderView就是渲染树的根节点.

final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];

void addPersistentFrameCallback(FrameCallback callback) {
  _persistentCallbacks.add(callback);
}

添加持久帧回调,持久帧回调一旦注册无法注销,在瞬态帧回调后执行,它们可以驱动渲染管道也就是渲染流水线,也就是说渲染流水线的主要阶段是由这个回调启动的. 这也是非常重要的一个回调.

下面这个方法就很简单了, MouseTracker鼠标更踪器

/// Creates a [MouseTracker] which manages state about currently connected
/// mice, for hover notification.
///
/// Used by testing framework to reinitialize the mouse tracker between tests.
@visibleForTesting
void initMouseTracker([MouseTracker? tracker]) {
  _mouseTracker?.dispose();
  _mouseTracker = tracker ?? MouseTracker();
}

创建一个MouseTracker鼠标更踪器,用于测试期间,简单知道即可

WidgetsBinding, 组件绑定

The glue between the widgets layer and the Flutter engine.

Widgets 和 Flutter 引擎之间的粘合剂,用来连接Widgetengine

/// The glue between the widgets layer and the Flutter engine.
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;
    platformDispatcher.onLocaleChanged = handleLocaleChanged;
    platformDispatcher.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    assert(() {
      FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
      return true;
    }());
    platformMenuDelegate = DefaultPlatformMenuDelegate();
  }
}

这个类首先创建了BuildOwner,它主要负责Widget的重建,并且设置了个onBuildScheduled回调.

这个类和RenderBinding中创建的PipelineOwner类是Flutter框架里的两个核心类,一个负责重新渲染,一个负责Widget重建.

SystemChannels.navigation设置的回调是用来处理路由的.

到这里,ensureInitialized()就查看完了,总体上做的主要操作就是绑定ui.PlatformDispatcher提供的各种各种回调和API,将它们封装到不同的绑定类中.

需要我们重点关注的是SchedulerBindingRenderBindingWidgetsBinding, 这三个都是和渲染相关的类.

scheduleAttachRootWidget

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


/// Takes a widget and attaches it to the [renderViewElement], creating it if
/// necessary.
///
/// This is called by [runApp] to configure the widget tree.
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();
  }
}

这个方法的作用就是获取一个小部件,添加到rendeViewElement, 这里的renderView就是在RenderBinding中创建的renderView()实例,也是渲染树render tree的根节点.

RenderView继承自RenderObject,而RenderObject需要由对应的WidgetElement

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].
  RenderObjectToWidgetAdapter({
    this.child,
    required this.container,
    this.debugShortDescription,
  }) : super(key: GlobalObjectKey(container));
}

@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;

RenderObjectToWidgetAdapter就是这个Widget,用于连接RenderObjectElement. 用于将widget, 也就是我们传在runApp(MyApp))传入的MyApp(),附加到renderView

RenderObjectToWidgetElement就是这个Element. 那么它是怎么和RenderView关联起来的呢, 可以看到上面createRenderObject返回的container,就是构造函数传入的RenderView.

我们自己传入的MyApp作为一个子Widget被RenderObjectToWidgetAdapter所持有.

RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
  if (element == null) {
    owner.lockState(() {
      element = createElement();
      assert(element != null);
      element!.assignOwner(owner);
    });
    owner.buildScope(element!, () {
      element!.mount(null, null);
    });
  } else {
    element._newWidget = this;
    element.markNeedsBuild();
  }
  return element!;
}

attachToRenderTree就是具体的执行操作了,对应的就是渲染流水线的的构建Build阶段,这是会根据widget生成应的element treerender tree, 这里的widget也就是RenderObjectToWidgetAdapter.

构建Build阶段完成以后, 接着就是布局Layout和绘制Paint阶段了.也就是runApp里最后一个函数的调用.

scheduleWarmUpFrame

void scheduleWarmUpFrame() {

  final bool hadScheduledFrame = _hasScheduledFrame;

  Timer.run(() {
    assert(_warmUpFrame);
    handleBeginFrame(null);
  });
  Timer.run(() {
    assert(_warmUpFrame);
    handleDrawFrame();
    
    if (hadScheduledFrame) {
      scheduleFrame();
    }
  
  });
}

这个函数位于SchedulerBinding中, 主要调用了两个函数,也是platformDispatcheronBeginFrameonDrawFrame回调.这里使用Timer.run的最用是为了微任务处理完之后,再来执行这两个函数.

总结

Flutter初始化框架到这里就介绍完了, 同时还介绍了Flutter首帧渲染的一个大致流程. Flutter框架初始化其实就是各种Binding的初始化,对 ui.PlatformDispatcher的一个封装. root widget 的一个创建和首帧渲染. Flutter 框架主要就是围绕渲染流水线ui.PlatformDispatcher这两个进行处理

本文要点:

  • SchedulerBinding,RenderBindingWidgetsBinding三个重要的绑定,
  • PipelineOwnerBuildOwner两个owner
  • 2棵树的根节点: render tree根节点RenderViewelement tree根节点RenderObjectToWidgetElement