• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Flutter启动流程

武飞扬头像
Android小兰
帮助1

一,Mixins

1,定义:Mixins 是一种在多个类层次结构中重用类代码的方法。

个人理解:就是一个类,这个类有一些方法,其他类可以在不继承这个类的情况下使用这个类的方法。

2,几个关键词

(1)mixin:一般使用mixin关键字定义可以混合的类;

(2)with:使用混合时用with关键字;

(1)on:添加限定条件,如下,意思是这个类只能被on的类或者子类混合

3,现在有这个情况:

  1.  
    class A{
  2.  
    A(){
  3.  
    print('A constructor');
  4.  
    }
  5.  
    log() => print('A log');
  6.  
    }
  7.  
    mixin AA on A{
  8.  
    log() {
  9.  
    print('AA log');
  10.  
    }
  11.  
    }
  12.  
    mixin AB on A{
  13.  
    @override
  14.  
    log() {
  15.  
    super.log();
  16.  
    print('AB log');
  17.  
    }
  18.  
    }
  19.  
    //C是A的子类,所以可以混合AA和AB
  20.  
    class C extends A with AA,AB{
  21.  
    @override
  22.  
    log() {
  23.  
    super.log();
  24.  
    print('C log');
  25.  
    }
  26.  
    }

类C继承类A,并且混合了AA和AB,这几个类都有log方法,现在执行:

C().log(),会输出什么?

答案是:

  1.  
    I/flutter (16574): A constructor
  2.  
    I/flutter (16574): AA log
  3.  
    I/flutter (16574): AB log
  4.  
    I/flutter (16574): C log

因为混合类时,进行混合的多个类是线性的,所以他们的共有方法不会冲突,会优先调用后面混合的类的方法,所以混合的顺序决定了混合时相同的方法的处理逻辑。

像这个例子,执行的方法肯定是C.log,但因为super.log(),所以会调用AB.log,AA.log,因为AA.log没有super,所以没有调用A.log。

mixin的类不能有构造函数,不能继承其他类,因为mixin机制语义上是向一个类混入其他类的方法或者成员变量,使得我们可以在混合类中访问到混入类的方法或者属性。而混入其他类的构造函数实际上是没有意义的,因为不会有人手动去调用这个混入类的构造函数。

二,runAPP()

从这儿开始执行:

  1.  
    runApp(const MyApp());
  2.  
     
  3.  
    void runApp(Widget app) {
  4.  
    //1,初始化
  5.  
    //2,绑定根节点
  6.  
    //3,绘制热身帧
  7.  
    WidgetsFlutterBinding.ensureInitialized()
  8.  
    ..scheduleAttachRootWidget(app)
  9.  
    ..scheduleWarmUpFrame();
  10.  
    }

2.1,初始化

2.1.1,初始化函数

看下初始化函数,这儿是WidgetsFlutterBinding的单例过程,因为要确保flutter完成初始化并且只完成一次,这儿调用了自己的构造器

  1.  
    static WidgetsBinding ensureInitialized() {
  2.  
    if (WidgetsBinding._instance == null)
  3.  
    WidgetsFlutterBinding();
  4.  
    return WidgetsBinding.instance;
  5.  
    }

对于WidgetsFlutterBinding这个类,继承了BindingBase并且混入了7个类:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding{

因为WidgetsFlutterBinding继承BindingBase,在调用构造方法前会先调用BindingBase的构造方法,所以看下父类BindingBase的构造方法:

  1.  
    BindingBase() {
  2.  
    developer.Timeline.startSync('Framework initialization');
  3.  
    //注意:这个方法,其他七个混入的类都有
  4.  
      //绑定初始化
  5.  
    initInstances();
  6.  
      //在绑定初始化完成后进行服务初始化
  7.  
    initServiceExtensions();
  8.  
    developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
  9.  
    developer.Timeline.finishSync();
  10.  
    }

因为initInstances()方法,其他几个混入的类都有,所以WidgetsBinding 会覆盖前面混入的 initInstances(),所以 WidgetsBinding 的 initInstances() 会首先被调用,而 WidgetsBinding 还有其他几个混入的类的 initInstances 函数中都执行了

super.initInstances();

所以根据mixin的特性 initInstances 的执行顺序依次是:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBinding -> WidgetsBinding,从而依次完成各个 Binding 的初始化相关工作。

2.1.2,七个Bindding

1,GestureBinding:手势事件绑定,主要处理触屏幕指针事件的分发以及事件最终回调处理。

2,SchedulerBinding:绘制调度绑定,和绘制渲染流程有关。

3,ServicesBinding:

(1)platform与flutter层通信相关服务的初始化,接收MethodChannelSystemChannels传递过来的消息;

(2)注册监听了flutter app的生命周期变化事件,根据生命周期状态决定是否允许发起绘制任务

4,PaintingBinding:和图片缓存有关。

5,SemanticsBinding:语义相关的初始化,主要就是描述应用程序中的UI信息,用于读屏使用。

6,RendererBinding:

(1)初始化了渲染管道PipelineOwner和RenderView,这就是我们屏幕真实显示的那个View,Flutter是单页面的UI框架,renderView就是这个时间点被初始化出来的。

(2)注意初始化方法中把renderView挂载到pipelineOwner.rootNode上,RenderView是RenderObject的子类,renderView其实是Render tree 的根节点,后续会讲到。

7,WidgetsBinding:

(1)初始化了一个BuildOwner对象,它主要是执行widget tree的build任务;

(2)执行了一些window的回调。

2.2,绑定根节点

2.2.1,接下来看绑定根节点的方法scheduleAttachRootWidget()

  1.  
    void scheduleAttachRootWidget(Widget rootWidget) {
  2.  
    Timer.run(() {
  3.  
    //传入了rootWidget,这个rootWidget就是我们构建的MyApp(),这个方法主要是绑定根节点
  4.  
    attachRootWidget(rootWidget);
  5.  
    });
  6.  
    }
  1.  
    /// 创建树的根节点
  2.  
    /// 这个方法主要是将根widget和根element和根renderObject关联起来
  3.  
    /// 并将唯一的BuildOwner对象引用作为根对象的持有对象,通过继承关系层层传递
  4.  
    void attachRootWidget(Widget rootWidget) {
  5.  
    final bool isBootstrapFrame = renderViewElement == null;
  6.  
    _readyToProduceFrames = true;
  7.  
    //1,初始化了一个RenderObjectToWidgetAdapter对象
  8.  
    //RenderObjectToWidgetAdapter继承RenderObjectWidget,所以本质是一个widget,可以说创建了widget树的根节点
  9.  
    //调用attachToRenderTree
  10.  
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
  11.  
    container: renderView,//之前介绍的在RendererBinding初始化的对象,也是renderObject的根节点
  12.  
    debugShortDescription: '[root]',
  13.  
    child: rootWidget,//根widget,就是runAPP传入的MyApp()
  14.  
    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
  15.  
    if (isBootstrapFrame) {
  16.  
    SchedulerBinding.instance.ensureVisualUpdate();
  17.  
    }
  18.  
    }
  19.  
     
  20.  
    RenderView get renderView => _pipelineOwner.rootNode! as RenderView;
  1.  
    /// 这儿创建了element树的根节点,并返回了element
  2.  
    /// 也就是_renderViewElement其实就是element树的根
  3.  
    RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
  4.  
    if (element == null) {
  5.  
    owner.lockState(() {
  6.  
    //创建element树的根节点 RenderObjectToWidgetElement
  7.  
    element = createElement();
  8.  
    assert(element != null);
  9.  
    //绑定BuildOwner,通过 BuildOwner 构建需要构建的 element
  10.  
    element!.assignOwner(owner);
  11.  
    });
  12.  
    //BuildOwner构建element
  13.  
    owner.buildScope(element!, () {
  14.  
    element!.mount(null, null);
  15.  
    });
  16.  
    } else {
  17.  
    element._newWidget = this;
  18.  
    element.markNeedsBuild();
  19.  
    }
  20.  
    return element!;
  21.  
    }

现在为止,出现了三棵树的根:

widget树:RenderObjectToWidgetAdapter对象;

element树:RenderObjectToWidgetElement对象;

renderObject树:RenderView对象。

接下来看mount方法,作用就是将element添加到树种的parent节点上。

  1.  
    //1,RenderObjectToWidgetElement:
  2.  
    @override
  3.  
    void mount(Element? parent, Object? newSlot) {
  4.  
    assert(parent == null);
  5.  
      //先看下mount方法都做了什么
  6.  
    super.mount(parent, newSlot);
  7.  
    //在这儿触发了_rebuild方法,该方法调用了updateChild方法
  8.  
    _rebuild();
  9.  
    assert(_child != null);
  10.  
    }
  11.  
     
  12.  
    //2, RenderObjectElement:
  13.  
      //创建了renderObject
  14.  
    @override
  15.  
    void mount(Element? parent, Object? newSlot) {
  16.  
    super.mount(parent, newSlot);
  17.  
    _renderObject = (widget as RenderObjectWidget).createRenderObject(this);
  18.  
    attachRenderObject(newSlot);
  19.  
    _dirty = false;
  20.  
    }
  21.  
     
  22.  
      //因为widget是RenderObjectToWidgetAdapter类型,其createRenderObject返回的了container
  23.  
    //这个container就是我们之前传的renderView
  24.  
      RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
  25.  
     
  26.  
      //将renderObject挂载到RenderObject Tree上
  27.  
    void attachRenderObject(Object? newSlot) {
  28.  
    _slot = newSlot;
  29.  
    _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
  30.  
    _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
  31.  
    final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
  32.  
    if (parentDataElement != null)
  33.  
    _updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>);
  34.  
    }
  35.  
     
  36.  
    //3, Element:
  37.  
      //首先执行的mount,主要是设置parent,slot等的值
  38.  
    void mount(Element? parent, Object? newSlot) {
  39.  
    _parent = parent;//element树上的父节点
  40.  
    _slot = newSlot;//子element在父节点上的位置
  41.  
    _lifecycleState = _ElementLifecycle.active;
  42.  
    _depth = _parent != null ? _parent!.depth 1 : 1;//element树的深度
  43.  
    if (parent != null) {
  44.  
    _owner = parent.owner;
  45.  
    }
  46.  
    final Key? key = widget.key;
  47.  
    if (key is GlobalKey) {
  48.  
    owner!._registerGlobalKey(key, this);
  49.  
    }
  50.  
    _updateInheritance();
  51.  
    attachNotificationTree();
  52.  
    }

以上mount执行顺序为Element.mount->RenderObjectElement.mount->RenderObjectToWidgetElement.mount,执行完毕后在Element.mount

函数这里会触发_rebuild();在_rebuild()里面我们看到了Element.updateChild()。

  1.  
    void _rebuild() {
  2.  
    try {
  3.  
           //_child为null,第二个入参就是MyApp()
  4.  
    _child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
  5.  
    } catch (exception, stack) {
  6.  
    ...
  7.  
    }
  8.  
    }

Element.updateChild()其实是element很重要的一个方法,作为widget和renderobject的桥梁,这个方法会根据不同的条件去创建、更新、删除element。

这里可见就是build子widget,这里就是build MyApp()

  1.  
    //这里传的child是null
  2.  
    Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
  3.  
    final Element newChild;
  4.  
    if (child != null) {
  5.  
    //.......
  6.  
    } else {
  7.  
    newChild = inflateWidget(newWidget, newSlot);
  8.  
    }
  9.  
     
  10.  
    return newChild;
  11.  
    }

updateChild里有调用inflateWidget方法,inflateWidget这个函数

  1.  
    Element inflateWidget(Widget newWidget, Object? newSlot) {
  2.  
    ...
  3.  
    try {
  4.  
    ...
  5.  
          //这儿就是为widget创建对应的element对象,所以widget和element关联起来了
  6.  
    final Element newChild = newWidget.createElement();
  7.  
          //又调用了mount方法,将子element挂载到当前element结点上
  8.  
    newChild.mount(this, newSlot);
  9.  
    return newChild;
  10.  
    } finally {
  11.  
    ...
  12.  
    }
  13.  
    }

在这个函数里面就会触发 createElement去创建element ,element又会去调用对应类的mount函数。

经过一系列的流程之后,又会回到inflateWidget这个函数中,再次触发新的mount函数,形成一个层层调用,不断创建parentElement到childElement的过程,这个过程完成了element tree的构建。

2.2.2,三棵树简介

WIdget树:存放渲染内容

element树:分离 WidgetTree 和真正的渲染对象的中间层, WidgetTree 用来描述对应的Element 属性,同时持有Widget和RenderObject,存放上下文信息,通过它来遍历视图树,支撑UI结构。

RenderObject树:用于应用界面的布局和绘制,负责真正的渲染,保存了元素的大小,布局等信息,实例化一个 RenderObject 是非常耗能的。

总结:

当应用启动时Flutter会遍历并创建所有的 Widget 形成 Widget Tree,通过调用 Widget 上的 createElement() 方法创建每个 Element 对象,形成 Element Tree。最后调用 Element 的 createRenderObject() 方法创建每个渲染对象,形成一个 Render Tree。

为什么要这样?

widget的重建开销非常小,所以可以随意的重建,因为它不一会导致页面重绘,并且它也不一定会常常变化。 而renderObject如果频繁创建和销毁成本就很高了,对性能的影响比较大,因此它会缓存所有页面元素,只是当这些元素有变化时才去重绘页面。

而判断页面有无变化就依靠element了,每次widget变化时element会比较前后两个widget,只有当某一个位置的Widget和新Widget不一致,才会重新创建Element和widget;其他时候则只会修改renderObject的配置而不会进行耗费性能的RenderObject的实例化工作了。

2.3,绘制热身帧

  1.  
    void scheduleWarmUpFrame() {
  2.  
    if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
  3.  
    return;
  4.  
     
  5.  
    _warmUpFrame = true;
  6.  
    final TimelineTask timelineTask = TimelineTask()..start('Warm-up frame');
  7.  
    final bool hadScheduledFrame = _hasScheduledFrame;
  8.  
     
  9.  
    Timer.run(() {
  10.  
    assert(_warmUpFrame);
  11.  
        //1,
  12.  
    handleBeginFrame(null);
  13.  
    });
  14.  
    Timer.run(() {
  15.  
    assert(_warmUpFrame);
  16.  
        //2,
  17.  
    handleDrawFrame();
  18.  
    resetEpoch();
  19.  
    _warmUpFrame = false;
  20.  
    if (hadScheduledFrame)
  21.  
    scheduleFrame();
  22.  
    });
  23.  
    lockEvents(() async {
  24.  
    await endOfFrame;
  25.  
    timelineTask.finish();
  26.  
    });
  27.  
    }

scheduleWarmUpFrame绘制的是根节点RenderObject对应的TransformLayer对象,调用handleBeginFrame和handleDrawFrame方法,通过pipelineOwner去执行layoutpaint等一些列操作。

并且scheduleWarmUpFrame是立即去绘制的,因为启动的显示要越快越好。

后面的lockEvents也是为了等待预约帧绘制完成后再去执行其他的任务。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfkkjbe
系列文章
更多 icon
同类精品
更多 icon
继续加载