1. 程式人生 > 實用技巧 >Flutter Framework啟動程式碼閱讀

Flutter Framework啟動程式碼閱讀

首先開啟啟動原生的應用,然後初始化flutter engine,之後在開啟4個Task Runners分別是
Platform Task Runner: 跟Flutter Engine的所有互動(介面呼叫)必須發生在Platform Thread,對應的native的主執行緒
UI Task Runner Thread(Dart Runner): UI Task Runner被Flutter Engine用於執行Dart root isolate程式碼
GPU Task Runner: 被用於執行裝置GPU的相關呼叫
FlutterEngine加之後, 開始執行main函式
IO Task Runner: 負責磁碟資料的讀取,主要是圖片,提前處理減輕GPU的壓力
下面主要是UI Task Runner

下的啟動過程,其他幾個TaskRunner大都在FlutterEngine層

Flutter main函式

它首先建立了一個MaterailApp,通常會叫它根檢視,其實最終它會作為真正的根檢視的第一個child
然後執行WidgetsFlutterBinding,繫結flutterEngine的相關callback事件
構建ElementTree和RenderTree, WidgetTree在我們程式碼裡就能看到了,由開發者調整。它們的先後順序是 WidgetTree -> ElementTree -> RenderTree
然後通知Flutter Engine預熱,重新整理當前檢視,而不是等待vsync訊號

void main() {
  runApp(
    MaterialApp(
      title: 'demo',
      home: HomePage(),
    ),
  );
}
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

1. 建立根RootWidget

1.1 這裡一般定義為MaterialApp, 它初始化了很多屬性它是一個使用material design的應用

const MaterialApp({
    Key key,
    this.navigatorKey,
    this.home,
    this.routes = const <String, WidgetBuilder>{},
    this.initialRoute,
    this.onGenerateRoute,
    this.onGenerateInitialRoutes,
    this.onUnknownRoute,
    this.navigatorObservers = const <NavigatorObserver>[],
    this.builder,
    this.title = '',
    this.onGenerateTitle,
    this.color,
    this.theme,
    this.darkTheme,
    this.themeMode = ThemeMode.system,
    this.locale, //app的初始化local
    this.localizationsDelegates,//設定app localizations的delegate,用於提供翻譯字串
    this.localeListResolutionCallback,//app的local修改後的回撥
    this.localeResolutionCallback,//用於攔截修改當前支援的local
    this.supportedLocales = const <Locale>[Locale('en', 'US')],//指定app支援哪些local,不指定則採用系統的local
    this.debugShowMaterialGrid = false,
    this.showPerformanceOverlay = false,
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,
    this.debugShowCheckedModeBanner = true,
    this.shortcuts,
    this.actions,
  })

1.2 MaterialApp中的資訊最終會傳遞給WdigetsApp, 具體實現在 _WidgetsAppState

  _WidgetsAppState
    @override
  void initState() {
    super.initState();
    //建立GlobalKey,快取rootnavigator
    _updateNavigator();
    //獲取系統的local資訊,並回調給materialApp
    _locale = _resolveLocales(WidgetsBinding.instance.window.locales, widget.supportedLocales);
    WidgetsBinding.instance.addObserver(this);
  }
  //建立Router
  Route<dynamic> _onGenerateRoute(RouteSettings settings) 
    ...
    widget.onGenerateRoute(settings);
  //建立unknownRoot
  Route<dynamic> _onUnknownRoute(RouteSettings settings) {

  //監聽native的導航事件
  Future<bool> didPopRoute() async //如果為android則為返回按鍵
  Future<bool> didPushRoute(String route) async

  //系統local處理
  void didChangeLocales(List<Locale> locales) {
    final Locale newLocale = _resolveLocales(locales, widget.supportedLocales);
    //處理local
    Locale _resolveLocales(List<Locale> preferredLocales, Iterable<Locale> supportedLocales) {
    // 過濾local,用於修改local,如果取不到對應的local則採用相近的local
    if (widget.localeListResolutionCallback != null) {
      final Locale locale = widget.localeListResolutionCallback(preferredLocales, widget.supportedLocales);
      //處理MaterialApp local回撥資訊,優先選擇系統的預設local
       if (widget.localeResolutionCallback != null) {
      final Locale locale = widget.localeResolutionCallback(
        preferredLocales != null && preferredLocales.isNotEmpty ? preferredLocales.first : null,
        widget.supportedLocales,
      );

1.3 根檢視執行build方法, 構建Element的配置資訊

  • 可以看到根檢視最終的Child的是一個 Navigator , 而 Navigator 會掛在一個 視覺化的rootWidget , 通過initialRoute去查詢。
Widget build(BuildContext context) {
    Widget navigator;
    if (_navigator != null) {
      navigator = Navigator(
        key: _navigator,
        //優先選擇native的routeName
        initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
            ? WidgetsBinding.instance.window.defaultRouteName
            : widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
        //設定route的構造方法
        onGenerateRoute: _onGenerateRoute,
        //設定initialRoute
        onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null
          ? Navigator.defaultGenerateInitialRoutes
          : (NavigatorState navigator, String initialRouteName) {
            return widget.onGenerateInitialRoutes(initialRouteName);
          },
        //設定位置的root
        onUnknownRoute: _onUnknownRoute,
        //設定navigator的觀察者
        observers: widget.navigatorObservers,
      );
    }

    Widget result;
    if (widget.builder != null) {
      result = Builder(
        builder: (BuildContext context) {
          return widget.builder(context, navigator);
        },
      );
    } else {
      assert(navigator != null);
      result = navigator;
    }
    //設定樣式,DefaultTextStyle繼承於InheritedTheme
    if (widget.textStyle != null) {
      result = DefaultTextStyle(
        style: widget.textStyle,
        child: result,
      );
    }
    ...
    Widget title;
    if (widget.onGenerateTitle != null) {
      //轉換child的context作用域,避免loaclizations widget構建失敗
      title = Builder(
        builder: (BuildContext context) {
          final String title = widget.onGenerateTitle(context);
          assert(title != null, 'onGenerateTitle must return a non-null String');
          return Title(
            title: title,
            color: widget.color,
            child: result,
          );
        },
      );
    } else {
      title = Title(
        title: widget.title,
        color: widget.color,
        child: result,
      );
    }

    final Locale appLocale = widget.locale != null
      ? _resolveLocales(<Locale>[widget.locale], widget.supportedLocales)
      : _locale;
    ...
    //捕捉使用者意圖
    return Shortcuts(
      shortcuts: widget.shortcuts ?? WidgetsApp.defaultShortcuts,
      debugLabel: '<Default WidgetsApp Shortcuts>',
      child: Actions(
        actions: widget.actions ?? WidgetsApp.defaultActions,
        child: FocusTraversalGroup(
          policy: ReadingOrderTraversalPolicy(),
          //獲取native螢幕大小,繫結dart.ui window回撥資訊(textScale/brightness)
          child: _MediaQueryFromWindow(
              //通過引入_LocalizationsScope包裝根檢視 `Title` ,同時提供了local data給它的所有子widget
            child: Localizations(
              locale: appLocale,
              delegates: _localizationsDelegates.toList(),
              child: title,
            ),
          ),
        ),
      ),
    );
  }

2. 繫結FlutterEngine相關服務擴充套件

  • 執行WidgetsFlutterBinding.ensureInitialized方法初始化, 在初始化方法中完成flutterEngine所有方法的繫結
  • 建立全域性單例類 WidgetsFlutterBinding , 對應程式碼如下
//WidgetsFlutterBinding初始化物件 `this` 賦值給了 `WidgetsBinding.instance` ,通過類屬性 `instance` 讓它成為了全域性單例子
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  • WidgetsFlutterBinding關係圖如下,通過mixin實現多重繼承完成了FlutterEngine相關的事件繫結,它的繫結事件按照如下with從左到右的順序一次繫結,由此可見 WidgetsBindings 依賴的 FlutterEngine`服務最多, 因此它需要最後再繫結.
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

2.1 BindingBase:

提供最基本的服務訂閱, 提供初始化 WidgetsBinding.instance 的方法

abstract class BindingBase {
 
  BindingBase() {
 //開啟一個同步執行的Timeline,初始化flutterWidgets執行所必須的的訂閱事件
 //作為一個base class為子類提供斷言
    developer.Timeline.startSync('Framework initialization');
    ...
    //初始化mixin相關的例項物件
    initInstances();
    ...
    //提供便利方法,執行 flutter engine提供的 `ext.flutter.$name` 方法
    initServiceExtensions();
    ...
    //通知native,widgets初始化完畢,可以開始繪製
    developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
    //解除Timeline的同步限制
    developer.Timeline.finishSync();
  }

  //flutter engine提供的視窗,用於繫結native的service,監聽flutter engine的回撥事件
  ui.Window get window => ui.window;
  
  //定義一個抽象的方法,供mixin類初始化例項物件
  @protected
  @mustCallSuper
  void initInstances() {
    assert(() {
      _debugInitialized = true;
      return true;
    }());
  }
  
  //註冊flutter engine的擴充套件資訊
  @protected
  @mustCallSuper
  void initServiceExtensions() {
    assert(!_debugServiceExtensionsRegistered);

    assert(() {
      //註冊dart vm的 hot reload事件,用於debug時快速更新app
      registerSignalServiceExtension(
        name: 'reassemble',
        callback: reassembleApplication,
      );
      return true;
    }());

    if (!kReleaseMode && !kIsWeb) {
        //註冊dart vm的退出事件
      registerSignalServiceExtension(
        name: 'exit',
        callback: _exitApplication,
      );
      registerServiceExtension(
        name: 'saveCompilationTrace',
        callback: (Map<String, String> parameters) async {
          return <String, dynamic>{
            'value': ui.saveCompilationTrace(),
          };
        },
      );
    }

    assert(() {
      const String platformOverrideExtensionName = 'platformOverride';
      //註冊platformOverride的回撥事件
      registerServiceExtension(
        name: platformOverrideExtensionName,
        callback: (Map<String, String> parameters) async {
          if (parameters.containsKey('value')) {
            switch (parameters['value']) {
              case 'android':
                debugDefaultTargetPlatformOverride = TargetPlatform.android;
                break;
              case 'fuchsia':
                debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
                break;
              case 'iOS':
                debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
                break;
              case 'linux':
                debugDefaultTargetPlatformOverride = TargetPlatform.linux;
                break;
              case 'macOS':
                debugDefaultTargetPlatformOverride = TargetPlatform.macOS;
                break;
              case 'windows':
                debugDefaultTargetPlatformOverride = TargetPlatform.windows;
                break;
              case 'default':
              default:
                debugDefaultTargetPlatformOverride = null;
            }
            _postExtensionStateChangedEvent(
              platformOverrideExtensionName,
              defaultTargetPlatform.toString().substring('$TargetPlatform.'.length),
            );
            await reassembleApplication();
          }
          return <String, dynamic>{
            'value': defaultTargetPlatform
                     .toString()
                     .substring('$TargetPlatform.'.length),
          };
        },
      );
      return true;
    }());
    assert(() {
      _debugServiceExtensionsRegistered = true;
      return true;
    }()); 
  }

  //定義Timeline的同步執行事件,通過lock數量控制是否將解除timeline的同步執行
  //對關鍵操作提供同步執行操作 
  @protected
  Future<void> lockEvents(Future<void> callback()) {
    developer.Timeline.startSync('Lock events');
//呼叫shell的退出命令,關閉程式
Future<void> _exitApplication() async {
  exit(0);
}

2.2 GestureBinding

  • 註冊手勢的點選事件回撥
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;
  }
  • 註冊MethodChannel的回撥

lifeCycle的監聽, frame重新整理

mixin ServicesBinding on BindingBase {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _defaultBinaryMessenger = createBinaryMessenger();
    window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    initLicenses();
    SystemChannels.system.setMessageHandler(handleSystemMessage);
  }

2.3 PaintingBinding

  • 繫結繪製事件
mixin PaintingBinding on BindingBase, ServicesBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _imageCache = createImageCache();
    if (shaderWarmUp != null) {
      shaderWarmUp.execute();
    }
  }

2.4 RendererBinding

  • 繫結Render事件, 初始化根檢視的 RenderObject , 它比較特殊, 不同於一般的RenderObject, 因為需要提供視窗的大小, 所以系統對它進行了一次包裝, 根據原始碼, 視窗的大小和deviceRatio就是在這個時候設定的
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
  RenderView({
    RenderBox child,
    @required ViewConfiguration configuration,
    @required ui.Window window,
  })

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    //初始化渲染管道owner,繫結flutter engine的繪製檢視的回撥事件
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    //繫結flutter engine的 文字大小,測量資訊,螢幕亮度,語義行為事件
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
      //初始化renderRootObject
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    //監聽系統的framecallback事件執行 `drawFrame`
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
  }

WidgetsBindings

  • 註冊flutter engine widget相關的事件
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    ...
    _buildOwner = BuildOwner();
    buildOwner.onBuildScheduled = _handleBuildScheduled;
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);

3. 裝配RootWidget

  • FlutterEngine 相關的Service繫結以後, 開始執行 scheduleAttachRootWidget , 這裡通過Scheduler一個時間間隔為0的task來執行RootWidget繫結, Timer內部包裝了一個_RootZone的環境,
//在初始化的時候根據deviceRatio和螢幕大小初始化Root renderObject,賦值給`PiplineOwner`
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  set renderView(RenderView value) {
    assert(value != null);
    _pipelineOwner.rootNode = value;
  }
  void initRenderView() {
    assert(renderView == null);
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
    renderView.prepareInitialFrame();
  }
  ...

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }
   
    void attachRootWidget(Widget rootWidget) {
    _readyToProduceFrames = true;
    //根據RootRenderObject和RootWidget建立真正的RootWidget
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView, //此處renderView是RenderBinding在初始化的時候根據視窗大小還有裝置畫素比建立,並賦值給`_pipelineOwner`
      debugShortDescription: '[root]',
      child: rootWidget, //runApp傳入的rootWidget
    ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
  } // buildOwner是在WidgetBindings建立,用於管理ElementTree更新

    RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {
        element = createElement();//建立RootElment
        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裝配到ElementTree中時建立RenderObject
abstract class RenderObjectElement extends Element {
    @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    assert(() {
      _debugDoingBuild = true;
      return true;
    }());
    _renderObject = widget.createRenderObject(this);
    ...
    //然後將renderObject插入到它的先組上面,如此迴圈,最終形成了ElementTree和RrenderTree, ElmentTree節點更觸發RenderTree節點的更新,Widget作為Element的配置資訊,觸發Element的更新。
  @override
  void attachRenderObject(dynamic newSlot) {
    assert(_ancestorRenderObjectElement == null);
    _slot = newSlot;
    _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
    _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
    final ParentDataElement<ParentData> parentDataElement = _findAncestorParentDataElement();
    if (parentDataElement != null)
      _updateParentData(parentDataElement.widget);
  }

根據上面的執行順序可以看出, RootWidget最先建立,然後是RootRenderObject(PiplelineOwner負責管理RenderTree),

4. scheduleWarmUpFrame

  • 向flutter主動請求重新整理,而不是被動的通過vsync重新整理
mixin SchedulerBinding on BindingBase, ServicesBinding {
  void scheduleWarmUpFrame() {
    if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
      return;

    _warmUpFrame = true;
    //用於開啟一個同步事件,最終會提交一個event task到flutter engine
    /**
    // timeline.dart
    external void _reportTaskEvent(int taskId, String phase, String category,
    String name, String argumentsAsJson);   
    */
    Timeline.startSync('Warm-up frame');
    final bool hadScheduledFrame = _hasScheduledFrame;
    // We use timers here to ensure that microtasks flush in between.
    Timer.run(() {
      assert(_warmUpFrame);
      handleBeginFrame(null); //通知flutter engine 重新整理
    });
    Timer.run(() {
      ...
      handleDrawFrame();
      resetEpoch();
      _warmUpFrame = false;
      if (hadScheduledFrame)
        scheduleFrame(); //  window.scheduleFrame(); ->  void scheduleFrame() native 'Window_scheduleFrame';
    }); 
    lockEvents(() async {
      await endOfFrame;
      Timeline.finishSync();
    });
  }

總結

Flutter App在啟動時主要乾了以下幾件事情:

  1. 開啟main isolate,在預設的mainZone內運行了main函式.
  2. 建立MaterialApp(或者是自定義的rootWidget)
  3. 繫結flutter engine相關的服務,系統點選事件,鍵盤,螢幕,亮度,字型,繪製回撥,輔助提示,vsync資訊,system.platform channels ...,詳細參照啟動時Widgetsbinding的幾個mixin類的initialServices方法和dart.ui
  4. 構建RenderTree和ElementTree
  5. 主動通知flutter engine重新整理螢幕