Flutter启动过程

988 阅读6分钟

先上代码:

void main() => runApp(MyApp());

//widgets/binding.dart
//1 
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()//1-1
    ..scheduleAttachRootWidget(app)//1-2
    ..scheduleWarmUpFrame();//1-3
}

//widgets/binding.dart
//2
static WidgetsBinding ensureInitialized() {
  if (WidgetsBinding.instance == null)
    WidgetsFlutterBinding();
  return WidgetsBinding.instance;
}

//foundation/binding.dart
//WidgetsFlutterBinding只有默认构造函数,所以实际执行的是父类BindingBase的构造器
//3
BindingBase() {
  initInstances();
  initServiceExtensions();
  developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
}

//widgets/binding.dart
//4
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
  Timer.run(() {
    attachRootWidget(rootWidget);
  });
}

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

//widgets/binding.dart
//6
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;
}

//framework.dart
//7
  void buildScope(Element context, [ VoidCallback callback ]) {
    if (callback == null && _dirtyElements.isEmpty)
      return;
    try {
      _scheduledFlushDirtyElements = true;
      if (callback != null) {
        _dirtyElementsNeedsResorting = false;
        callback();
      }
      _dirtyElements.sort(Element._sort);
      _dirtyElementsNeedsResorting = false;
      int dirtyCount = _dirtyElements.length;
      int index = 0;
      while (index < dirtyCount) {
        _dirtyElements[index].rebuild();
        index += 1;
        if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
          _dirtyElements.sort(Element._sort);
          _dirtyElementsNeedsResorting = false;
          dirtyCount = _dirtyElements.length;
          while (index > 0 && _dirtyElements[index - 1].dirty) {
            index -= 1;
          }
        }
      }
    } finally {
      for (final Element element in _dirtyElements) {
        assert(element._inDirtyList);
        element._inDirtyList = false;
      }
      _dirtyElements.clear();
      _scheduledFlushDirtyElements = false;
      _dirtyElementsNeedsResorting = null;
    }
  }

//framework.dart
//8
void markNeedsBuild() {
  if (!_active)
    return;
  if (dirty)
    return;
  _dirty = true;
  owner.scheduleBuildFor(this);
}
  
//framework.dart
//9
void scheduleBuildFor(Element element) {
  if (element._inDirtyList) {
    _dirtyElementsNeedsResorting = true;
    return;
  }
  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
    _scheduledFlushDirtyElements = true;
    onBuildScheduled();
  }
  _dirtyElements.add(element);
  element._inDirtyList = true;
}
  
//scheduler/binding.dart
//10
void scheduleWarmUpFrame() {
  if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
    return;
  _warmUpFrame = true;
  Timeline.startSync('Warm-up frame');
  final bool hadScheduledFrame = _hasScheduledFrame;
  // We use timers here to ensure that microtasks flush in between.
  Timer.run(() {
    handleBeginFrame(null);
  });
  Timer.run(() {
    handleDrawFrame();
    resetEpoch();
    _warmUpFrame = false;
    if (hadScheduledFrame)
      scheduleFrame();
  });

  // Lock events so touch events etc don't insert themselves until the
  // scheduled frame has finished.
  lockEvents(() async {
    await endOfFrame;
    Timeline.finishSync();
  });
}

解释每个方法的作用

  1. runApp()

    这个方法是我们Flutter程序的入口

  2. ensureInitialized()

    这个方法执行flutter的一些初始化工作

    在这个方法里,第一次初始化WidgetsBinding.instance肯定是空的,所以执行了WidgetsBinding的构造器,而WidgetsBinding是默认的构造器,所以我们看它的父类BindingBase的构造器

  3. BindingBase()

    在这里做了一些系统服务的初始化工作,详细的请自己去看代码,这里略过。这时候注释1-1就执行完了。

  4. attachRootWidget(注释1里的scheduleAttachRootWidget()调用了该方法)

    该方法把我们传入到runApp方法里的参数作为child挂载到rootWidget上

    这里方法里有个类叫做RenderObjectToWidgetAdapter,但是不要被它的名字迷惑了,实际这是一个RenderObjectWidget的子类,而RenderObjectToWidgetAdapter这个类很重要,这个类相当于Android里的DecorView,也是rootWidget

  5. attachToRenderTree()

    该方法创建了RootElement和RootRenderObject。

    在第一次运行该方法的时候,element是null,所以会进入if这个分支里,这个方法里的createElement()方法创建了RenderObjectToWidgetAdapter对应的RootElement,然后element.assignOwner(owner)这句代码保存了BuildOwner这个引用(这个BuildOwner只有一个实例,是在WidgetsBinding的initInstances()方法里创建的,这个BuildOwner用来管理Element),然后执行owner.buildScope方法后执行SchedulerBinding.instance.ensureVisualUpdate()方法,最终会执行到window的scheduleFrame方法通知Engine下次 vsync 信号回调时,完成这些dirty元素的更新(Element初始化后_dirty默认为true)

  6. owner.buildScope

    该方法调用element的mount方法挂载element并调用_dirtyElements的rebuild方法。

    这个方法首先调用我们传入的callback方法,而我们传入的callback会执行element.mount(null, null),这个mount方法最终会调用到RenderObjectElement的mount方法,在这个方法里有一句代码是widget.createRenderObject(this),该方法创建了RenderObject。然后下面还有一句代码attachRenderObject(newSlot),该方法将刚才生成的RenderObject挂载到了RenderObject树中的某个slot,因为它是RenderObjectElement所以其实这个刚生成的RenderObject就作为了RenderObject树的根节点。然后我们继续看buildScope这个方法做了什么,在这个方法里,会先调用_dirtyElements.sort(Element._sort)对Element排序,因为如果先build child的话,在build parent的时候会再执行一遍child的build方法,所以这里做一次排序,然后把标记为dirty的Element挨个执行rebuild方法,但是,下面还有一个if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting)判断,这是因为在rebuild的时候,可能有新的dirty的element加入到_dirtyElements列表里,所以我们要对_dirtyElements再次排序,并找到列表最前面不是dirty的element的index(在第一次执行dirty的element的rebuild方法后这个element就不是dirty的了),然后从这个index再次往后执行element的rebuild方法。

  7. attachToRenderTree()剩余代码

    然后我们回到该方法,当有了RootElement,再调用该方法时会进入该方法的else分支,else里有这样两句代码

    element._newWidget = this;
    element.markNeedsBuild();
    

    也就是说,如果执行该方法时已经有了RootElement,RenderObjectToWidgetAdapter把自己赋值给自己对应的element的_newWidget属性,然后标记这个element为dirty。

  8. markNeedsBuild()

    这个方法会调用owner.scheduleBuildFor(this)方法,通知owner刷新页面

  9. scheduleBuildFor()

    该方法里会先执行onBuildScheduled()方法,再把当前element添加到_dirtyElements列表里,而onBuildScheduled这个方法是WidgetsBinding的initInstances方法里赋值的,所以这个方法实际上是WidgetsBinding类的_handleBuildScheduled方法,而这个方法会调用ensureVisualUpdate并最终会执行到SchedulerBinding类的scheduleFrame方法

  10. scheduleFrame()

  void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled)
      return;
    ensureFrameCallbacksRegistered();
    window.scheduleFrame();
    _hasScheduledFrame = true;
  }

在这个方法里,会执行window.scheduleFrame()方法,该方法是Dart Engine 底层的一个方法,它会触发 vsync 信号,以便在下次 vsync 信号回调时,完成这些dirty元素的更新。

我下面画了这么个流程,把最上面单独的方法串联了起来

runApp 入口

-> WidgetsFlutterBinding.scheduleAttachRootWidget 

-> WidgetsFlutterBinding.attachRootWidget

    创建了RenderObjectToWidgetAdapter这个RootWidget,然后把我们传入的app作为它的child

-> RenderObjectToWidgetAdapter.attachToRenderTree

    该方法有2个分支:
    
    1. 系统初始化时element参数为null,所以方法中通过createElement()创建了Flutter的第一个Element(该element是RenderObjectToWidgetAdapter对应的RenderObjectToWidgetElement),然后调用该element的mount方法和_rebuild方法
        1.1 RenderObjectToWidgetElement.mount方法和RenderObjectToWidgetElement._rebuild方法
            1.1.1 -> RenderObjectToWidgetElement.mount
                    该方法调用了当前element父节点(RootRenderObjectElement)的mount方法和它自己的_rebuild()方法
                    
                    -> RootRenderObjectElement.mount
                        -> RenderObjectElement.mount
                        该方法创建了Flutter的第一个RenderObject,该方法内部会调用自己的attachRenderObject方法
                        
                            -> RenderObjectElement.attachRenderObject
                            因为上面创建的是Flutter的第一个RenderObject,所以该方法内部其实什么都没做。(如果不是第一个节点(根节点)的话,会找到当前RenderObjectElement的上一级的RenderObjectElement节点,然后把当前RenderObjectElement插到上个节点中,并委托上个节点设置自己的parentData属性)

            1.1.2 -> RenderObjectToWidgetElement._rebuild
                    该方法内部调用updateChild(Element child, Widget newWidget, dynamic newSlot)方法,该方法根据不同条件来决定是删除自身还是更新自身
                    
        1.2 完成上面的1.1.1和1.1.2的操作后,通过owner.buildScope方法来调用_dirtyElements列表中element的rebuild()方法,然后调用SchedulerBinding.instance.ensureVisualUpdate(),最终会调用window.scheduleFrame()通知系统刷新第一帧;
        
    2. 当element不为null时的调用处没找到;当element不为null时,调用该element的markNeedsBuild()方法,该方法内部会调用owner.scheduleBuildFor(this)
        -> BuildOwner.scheduleBuildFor
        1. 先执行onBuildScheduled()方法,该方法是WidgetsBinding.initInstances()方法中设置的,该方法的实现是WidgetsBinding._handleBuildScheduled()
        2. 把scheduleBuildFor方法中的参数添加到_dirtyElements集合中
        
            -> WidgetsBinding._handleBuildScheduled
            该方法调用SchedulerBinding.instance.ensureVisualUpdate(),最终会调用window.scheduleFrame()通知系统刷新下一帧;同上面的1.2最后的调用一样

-> RenderObjectToWidgetAdapter.scheduleWarmUpFrame
    这个方法我没仔细看,大概是通知系统开始绘制第一帧的意思

总结

runApp方法里,在调用WidgetsFlutterBinding.scheduleAttachRootWidget这个方法时,我们创建了RenderObjectToWidgetAdapter这个RootWidget,然后把我们传入的app作为它的child,再然后RenderObjectToWidgetAdapter调用了它自己的attachToRenderTree方法,该方法中通过createElement()创建了RootElement,然后这个element调用它的mount方法创建了RootRenderObject,然后WidgetsFlutterBinding.scheduleAttachRootWidget调用SchedulerBinding.instance.ensureVisualUpdate()刷新第一帧。

疑问

我看到这里有个地方不清楚,有知道的小伙伴帮我解答一下,就是attachToRenderTree这个方法的什么时候会进入else方法快,我查了一下没找到哪里可以进入这个else,请知道的小伙伴指点一下,谢谢

扩展知识:

深入理解setState更新机制