开启成长之旅!这是我参加「日新计划 12 月更文挑战」的第1天,点击查看活动概况

flutter作为一个跨平台的框架,在制作上体现出了它跨平台的良好功能.那么,它是如何从runApp()后 制作上屏的呢?本文将与你一同去探索这一过程.
ps: 为了思想不中断, 本文仅对全体流程作剖析,不会深入剖析具体完成

起源

咱们运转一个flutter app ,进口一定是从runApp() 中进行的. 那么flutter 在runApp() 中做了哪些处理呢? 首要,咱们从runApp() 这个函数聊起.它是一个需要传入Widget 的函数.而传入的Widget ,即首屏烘托所需的Widget.

在此咱们应该知道这个概念, 即widget 是flutter 中用来描绘ui如何制作的配置文件,去形容一个组件在全体中的方位、大小.

那么不难揣度出.在runApp() 的过程中,假如Widget是制作的配置文件. 那么手势注册、桢调度等都应该是在此刻注册的. 带着这样的揣度咱们去源码中找答案.

剖析预备

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
  // 这儿解释下:
  // ..是flutter中的级联运算符
  // 能够同一个目标上连续调用多个目标的变量或办法
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

runApp() 中能够看到, 这儿实际上也便是调用了三个办法,以下咱们对每个办法进行刨析.

ensureInitialized

从字面意思看,这是为了保证已经初始化而调用的办法.它的效果是为了回来WidgetsBinding的目标.假如了解单列形式的话,会发现这么写实际上便是一个单列形式.

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

这儿咱们去发掘一下WidgetsFlutterBinding内部的结构函数在初始化时做了什么? 它是继承BingingBase的, 咱们进入BingingBase中浅看一下大约的完成, Timeline 和assert这部分代码咱们能够忽略.

ps: assert 在release代码中不会履行

也便是实际上的结构是这样的

 BindingBase() {
    initInstances();
    initServiceExtensions();
  }
  • initInstances() 办法是为了绑定初始化实例和其他的一些状况.
  • initServiceExtensions() 办法是为了绑定初始化服务

这儿咱们回过头来看WidgetsFlutterBinding它的一些完成接口, 顺序依次是:

接口 解释
GestureBinding 完成点击射中测试
SchedulerBinding 引入了帧的概念
ServicesBinding 提供对插件的拜访
PaintingBinding 解码图像
SemanticsBinding 语义树
RendererBinding 处理render tree
WidgetsBinding 处理widget tree

内部的具体完成这儿不再赘述,后续会逐章对这些进行分解、解释.这儿只是去剖析全体的流程. 也便是说, 在这儿咱们完成了对app系统的初始化、推动界面的制作,获取手势等等.

scheduleAttachRootWidget

在一系列服务注册完之后,咱们需要把当时的root widget 挂载到树上,

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

attachRootWidget 的办法中, 咱们构建了RenderObjectToWidgetAdapter 的目标. 经过RenderObjectToWidgetAdapter 当作ElementRenderObject 之间的桥梁,

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

同时,这儿依据renderViewElement 有没有赋值来判别是否是第一次加载.假如是第一次加载页面,会通知界面去刷新ui

scheduleWarmUpFrame

这个办法从字面意思来看 应该是在界面启动时去履行的一些办法. 首要,咱们看一下它的一些引证路径.发现一共有三个地方的代码都引证了这个办法

Flutter runApp 到渲染上屏
能够看到调用的三个地方分别是:

  • allowFirstFrame()
  • performReassemble()
  • runApp()

好家伙,这不都是类似于制作的进口函数? 因此,咱们能够揣度这儿便是一些制作时初始化时分必须履行的一些代码.

{
    // 这儿经过伪代码扼要了解一下大致完成
    // 
    if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) return;
    _warmUpFrame = true;
    // 这儿把源代码提前了,由于handle*()的代码都是经过Timer.run履行的,实际上是
    // 一种异步履行,会在下一帧去调用
    lockEvents(() async {
      await endOfFrame;
      timelineTask.finish();
    });
    // 开始帧回调
    handleBeginFrame(null);
    // 新帧回调处理
    handleDrawFrame();
    // 这儿和热重载相关
    resetEpoch();
    _warmUpFrame = false;
    // 
    if (hadScheduledFrame) scheduleFrame();
}

总结

总结一下, runApp() 经过

  1. 注册各种服务
  2. 注册ui
  3. 制作上屏

最终在屏幕上呈现出ui,其中还有如PipelineOwnerBuildOwner等等非常重要的api,这儿暂且不表. 后续咱们单章具体介绍,希望这一次与你一同阅读的思路能够帮你一同考虑启动的流程.假如想要了解更具体的内容,无妨点个重视重视后续的文章哦!