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

前言

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

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

文章后方也有一个流程图,能够经过看流程图了解app的启动流程函数

runApp作了什么?

咱们的main函数里面,一般都会调用runApp(Widget app)方法,那这个方法里面到底作了什么呢?runApp的实现以下布局

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

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

Flutter中的Binding

Flutter中的Binding概念我理解为一个胶水层,负责链接framework和engine层,关系大体以下ui

1.png

runApp方法就是建立一个胶水层WidgetsFlutterBinding,并链接framework和engine层。this

WidgetsFlutterBinding

WidgetsFlutterBinding是一个Binding的实例,也就是一条桥,负责链接Flutter的engine和framework的之间的事件交互。WidgetsFlutterBinding的定义以下spa

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这个类debug

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

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进行渲染
相关文章
相关标签/搜索