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進行渲染
相關文章
相關標籤/搜索