Flutter源码阅读(2)-Flutter App的启动流程

2,731 阅读6分钟

前言

从上一篇文章 Widget,Element,RenderObject树的构建和更新流程 分析了Flutter中的节点的构建,从源码出发,说明节点是如何一步步地往下构建,在文章也提到了WidgetsFlutterBinding,但是没有细说。

在这篇文章里面,主要分析App的构建过程,分析从runApp以后发生了什么事情。分析一下WidgetsFlutterBinding是什么东西

文章后方也有一个流程图,可以通过看流程图了解app的启动流程

runApp做了什么?

我们的main函数里面,通常都会调用runApp(Widget app)方法,那这个方法里面到底做了什么呢?runApp的实现如下

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()//初始化
    ..scheduleAttachRootWidget(app)//绑定根widget
    ..scheduleWarmUpFrame();//确保尽快第一帧
}

这个方法里面调用了WidgetsFlutterBinding的三个方法,分别用于初始化WidgetsFlutterBinding,和绑定widget,并开始构建页面。先分析一下这个WidgetsFlutterBinding是什么东西。

Flutter中的Binding

Flutter中的Binding概念我理解为一个胶水层,负责连接framework和engine层,关系大致如下

1.png

runApp方法就是创建一个胶水层WidgetsFlutterBinding,并连接framework和engine层。

WidgetsFlutterBinding

WidgetsFlutterBinding是一个Binding的实例,也就是一条桥,负责连接Flutter的engine和framework的之间的事件交互。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类,而且这些mixin都是继承于BindingBase这个类

BindingBase这个类的方法比较多,抽出来与本文相关的一些方法,如下

ui.SingletonFlutterWindow get window => ui.window;
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
void initInstances();
void initServiceExtensions();

其中的这个window是SingletonFlutterWindow类型的,位于Flutter 的engine层。SingletonFlutterWindow就是我们Flutter app加载的窗口,而且SingletonFlutterWindow是一个单例,它继承于FlutterView。

FlutterView中有一个方法

PlatformDispatcher get platformDispatcher;

这个方法返回一个platformDispatcher,这个platformDispatcher是Flutter 的一个事件分发器,负责Flutter分发engine的事件,和传递事件给engine层。

SingletonFlutterWindow继承自FlutterView。也就继承了platformDispatcher。也就是说SingletonFlutterWindow是通过platformDispatcher去处理来自Flutter engine层的事件, platformDispatcher将flutter engine的事件分发到SingletonFlutterWindow,把SingletonFlutterWindow的事件传给flutter engine。

注意一下,BindingBase里面也有一个platformDispatcher,BindingBase和FlutterView中的platformDispatcher是同一个。

WidgetsFlutterBinding.ensureInitialized()

现在说回WidgetsFlutterBinding,当在runApp中调用了WidgetsFlutterBinding.ensureInitialized的时候,会返回一个instance.如果instance为空,则调用默认的构造函数去创建一个instance。

默认构造函数在BindingBase的实现如下

 BindingBase() {
    ...
    initInstances();//实例化
    ...
    initServiceExtensions();//注册服务
    ...
  }

因为WidgetsFlutterBinding最后with的是WidgetsBinding了,WidgetsBinding是继承了BindingBase,ServicesBinding,SchedulerBinding,PaintingBinding,GestureBinding,RendererBinding,SemanticsBinding。所以这些mixin的initInstances和initServiceExtensions方法也会一并执行。WidgetsFlutterBinding和其with的mixin都会一同实例化并注册服务。

其中各个mixin负责的部分如下

  • BindingBase 各个binding相关的mixin的基类
  • ServicesBinding 处理与原生的交互通道
  • GestureBinding 处理手势
  • RendererBinding 处理渲染
  • PaintingBinding 处理绘制相关
  • SemanticsBinding 处理语义化
  • WidgetsBinding 处理widget

其中需要注意的是

  • WidgetsBinding中的initInstances会初始化一个BuildOwner,处于管理widget和Element树
  • RendererBinding中的initInstances会初始化一个PipelineOwner,用于管理RenderObjct树,也会去创建一个callback,这个callback在每一帧刷新的时候都会调用

binding层和SingletonFlutterWindow,WidgetsFlutterBinding做交互。其中WidgetsFlutterBinding中的mixin都负责某一块的功能。

大致关系如下

2.png

WidgetsFlutterBinding.scheduleAttachRootWidget

WidgetsFlutterBinding实例化后会继续调用scheduleAttachRootWidget方法。实现如下

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

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

在上一篇文章我们说过attachRootWidget方法用于是为根widget生成一个根Element.生成Element调用了attachToRenderTree方法并传入了BuildOwner和Element. attachRootWidget的实现如下

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);
      });
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance!.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element!;
  }

这里的处理逻辑我在上一篇文章也讲到过,具体关于节点树的构建可以参看上一篇文章。当element为空时,会调用SchedulerBinding.instance!.ensureVisualUpdate()的方法。这一个方法也很重要,经过下面的过程

ensureVisualUpdate() -> scheduleFrame() -> ensureFrameCallbacksRegistered()

最终会调用ensureFrameCallbacksRegistered方法,ensureFrameCallbacksRegistered方法如下

void ensureFrameCallbacksRegistered() {
    window.onBeginFrame ??= _handleBeginFrame;
    window.onDrawFrame ??= _handleDrawFrame;
  }

经过一些列的调用,最终会把SchedulerBinding中的_handleBeginFrame和_handleDrawFrame这两个callback传给WidgetsFlutterBinding类的window

set onBeginFrame(FrameCallback? callback) {
    platformDispatcher.onBeginFrame = callback;
  }
set onDrawFrame(VoidCallback? callback) {
    platformDispatcher.onDrawFrame = callback;

可以看到,window中的方法会把callback传给platformDispatcher。 也就是说WidgetsFlutterBinding.scheduleAttachRootWidget这个方法经过一系列的方法调用后,最终会把SchedulerBinding这个mixin的_handleBeginFrame和_handleDrawFrame传给platformDispatcher。

前面说到,platformDispatcher分发来自enginee的事件。而在这里SingletonFlutterWindow把platformDispatcher的onBeginFrame和onDrawFrame这两个事件交给SchedulerBinding处理。

当硬件发出VSync信号时,会调用platformDispatcher的onDrawFrame。从上面可知,实际上会调用SchedulerBinding中的_handleDrawFrame方法。_handleDrawFrame会调用handleDrawFrame方法。handleDrawFrame方法定义如下

 void handleDrawFrame() {
    ...
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (final FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
   ...

其中的_postFrameCallbacks里面存储的是callback,作用是硬件每次发出VSync信号的时候都会调用。

这个方法里面会取出_postFrameCallbacks里面所有的callback,然后执行callback。这里的_postFrameCallbacks是在RenderBinding这个mixin的initInstances方法中传入的,如下

void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    ...
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    ...
  }

在RenderBinding的initInstances方法做了两件事

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

因为在WidgetsBinding中实现了drawFrame方法,所以会调用WidgetsBinding中的drawFrame方法。 如下

void drawFrame() {
    ...
      if (renderViewElement != null)
        buildOwner!.buildScope(renderViewElement!);//构建更新子树
      super.drawFrame();
      buildOwner!.finalizeTree();告诉buildOwner可以完成更新了,可以释放掉_inactiveElements
   ...

这个方法会先调用buildOwnerOwner的buildScope方法去构建和更新节点树。然后调用super.drawFrame()方法去调用RederBinding的drawFrame方法。如下

void drawFrame() {
    assert(renderView != null);
    pipelineOwner.flushLayout();//刷新布局
    pipelineOwner.flushCompositingBits();//图层更新
    pipelineOwner.flushPaint();//刷新绘制
    if (sendFramesToEngine) {
      renderView.compositeFrame(); // 告诉GPU渲染renderView
      pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
      _firstFrameSent = true;
    }
  }

这个方法会去调用去管理RenderObject树生成新的UI绘制信息,交给renderView,并生成一个Scene给SingletonFlutterWindow,然后GPU去渲染新的界面

硬件每次发出VSync信号处理流程如下

3.png

关于图片中的一些补充

  • BuildOwner是在WidgetsnBiding的initInstances方法中生成的
  • PielineOwner是在RenderBinding的initInstances方法中生成的
  • RenderView是在RenderBinding的initInstances方法中生成的

WidgetsFlutterBinding.ensureInitialized()

这个方法最重要就是调用window.scheduleFrame()方法,从而调用platformDispatcher.scheduleFrame()方法,去通知engine层需要绘制。engine会根据情况尽快地调用platformDispatcher的onDrawFrame方法。

顺便一提的是,当我们开发过程中调用了setState()方法的时候,会调用BuildOwner的scheduleBuildFor方法,scheduleBuildFor方法中会调用onBuildScheduled这个callback,onBuildScheduled是由WigetsBinding这个mixin中传入的。对应着WigetsBinding的_handleBuildScheduled这个方法。

_handleBuildScheduled这个方法最终也是会调用window.scheduleFrame()通知engine更新界面。

总结

runApp这个方法总的来说就是做了以下的事情

  1. 在Flutter的framework层和engine层建立一个连接WidgetsFlutterBinding,注册Vsync回调后,每一帧调用的时候都会触发WidgetsFlutterBinding里面的方法,从而去调用framework层的处理逻辑
  2. 为传入的widget构建节点树,将节点树中的RenderObjct树的结果交给enginee层的SingletonFlutterWindow,然后通知到GPU进行渲染