Flutter APP 啟動(dòng)過(guò)程源碼分析

提示:本文設(shè)計(jì)到的 Flutter framework 層源碼是基于 Flutter 1.20.0

void main() => runApp(MyApp());
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

三行代碼代表了Flutter APP 啟動(dòng)的三個(gè)主流程:

  1. binding初始化(ensureInitialized)
  2. 綁定根節(jié)點(diǎn)(scheduleAttachRootWidget)
  3. 繪制熱身幀(scheduleWarmUpFrame)

1 binding初始化(ensureInitialized)

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

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

WidgetsFlutterBinding的ensureInitialized()其實(shí)就是一個(gè)獲取WidgetsFlutterBinding單例的過(guò)程,真正的初始化實(shí)現(xiàn)代碼在其7個(gè)mixin中。7個(gè)mixin按照嚴(yán)格的先后調(diào)用鏈關(guān)系完成不同 binding 的初始化。

WidgetsFlutterBinding繼承了BindingBase,在調(diào)用自己的構(gòu)造器之前會(huì)先先執(zhí)行了父類(lèi)BindingBase構(gòu)造函數(shù)。

  BindingBase() {
    developer.Timeline.startSync('Framework initialization');

    assert(!_debugInitialized);
    initInstances();
    assert(_debugInitialized);

    assert(!_debugServiceExtensionsRegistered);
    initServiceExtensions();
    assert(_debugServiceExtensionsRegistered);

    developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});

    developer.Timeline.finishSync();
  }

這里會(huì)調(diào)用initInstances() ,由于7個(gè) mixin 都重寫(xiě)了initInstances(), with 的最后一個(gè) WidgetsBinding 覆蓋了前面的 binding 的 initInstances(),所以 WidgetsBinding 的 initInstances() 會(huì)首先被調(diào)用,而 WidgetsBinding 的 initInstances 函數(shù)中先通過(guò) super 向上調(diào)用 initInstances ,所以 initInstances 的執(zhí)行順序依次是:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBinding -> WidgetsBinding,從而依次完成各個(gè) Binding 的初始化相關(guān)工作。

1.1 GestureBinding

手勢(shì)事件綁定,主要處理觸屏幕指針事件的分發(fā)以及事件最終回調(diào)處理。

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;
  }

這里將事件處理回調(diào) _handlePointerDataPacket 賦值給 window,供window 收到屏幕指針事件后調(diào)用。window類(lèi)是framework層與engine層處理屏幕相關(guān)事件的橋梁。_handlePointerDataPacket中會(huì)先調(diào)用hitTest進(jìn)行命中測(cè)試。GestureBinding及RenderBinding都實(shí)現(xiàn)了hitTest方法,按照mixin順序會(huì)優(yōu)先調(diào)用RenderBinding.hitTest。RenderBinding.hitTest會(huì)從renderTree的根節(jié)點(diǎn)遞歸調(diào)用命中測(cè)試,返回命中的深度最大的節(jié)點(diǎn)到根節(jié)點(diǎn)路徑上的所有節(jié)點(diǎn)。然后再執(zhí)行dispatchEvent根據(jù)返回的hitTest命中節(jié)點(diǎn)列表遍歷分發(fā)事件,事件分發(fā)的順序是先子節(jié)點(diǎn)后父節(jié)點(diǎn)最終到根節(jié)點(diǎn),類(lèi)似Android的事件冒泡機(jī)制。

1.2 SchedulerBinding

繪制調(diào)度綁定

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;

    // debug編譯模式時(shí)統(tǒng)計(jì)繪制流程時(shí)長(zhǎng),開(kāi)始、運(yùn)行、構(gòu)建、光柵化。
    if (!kReleaseMode) {
      int frameNumber = 0;
      addTimingsCallback((List<FrameTiming> timings) {
        for (final FrameTiming frameTiming in timings) {
          frameNumber += 1;
          _profileFramePostEvent(frameNumber, frameTiming);
        }
      });
    }
  }

SchedulerBinding的作用就是在debug編譯模式時(shí)統(tǒng)計(jì)繪制流程時(shí)長(zhǎng),開(kāi)始、運(yùn)行、構(gòu)建、光柵化。

1.3 ServicesBinding

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    
    // 構(gòu)建一個(gè)用于platform與flutter層通信的 BinaryMessenger
    _defaultBinaryMessenger = createBinaryMessenger();
    
    // 設(shè)置window監(jiān)聽(tīng)回調(diào),處理platform發(fā)送的消息
    window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    initLicenses();
    
    // 設(shè)置處理platform發(fā)送的系統(tǒng)消息的 Handler
    SystemChannels.system.setMessageHandler(handleSystemMessage);
    
    // 設(shè)置AppLifecycleState生命周期回調(diào)
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
    
    // AppLifecycleState 為 resumed 和 inactive 時(shí)才允許響應(yīng)Vsync信號(hào)進(jìn)行繪制
    readInitialLifecycleStateFromNativeWindow();
  }

ServicesBinding的初始化的工作主要是兩個(gè):

  1. platform與flutter層通信相關(guān)服務(wù)的初始化
  2. 注冊(cè)監(jiān)聽(tīng)了flutter app的生命周期變化事件,根據(jù)生命周期狀態(tài)決定是否允許發(fā)起繪制任務(wù)

設(shè)置處理 system 消息 handleSystemMessage,ServicesBinding 的handleSystemMessage 主要工作是異步處理系統(tǒng)發(fā)送的內(nèi)存緊張信號(hào),PaintingBinding 也實(shí)現(xiàn)了該方法。由于 PaintingBinding 被 with 的順序在 ServicesBinding 后面,所以 PaintingBinding 的 handleSystemMessage 會(huì)覆蓋 ServicesBinding 的 handleSystemMessage 被調(diào)用

  @override
  Future<void> handleSystemMessage(Object systemMessage) async {
    await super.handleSystemMessage(systemMessage);
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
    switch (type) {
      case 'fontsChange':
        _systemFonts.notifyListeners();
        break;
    }
    return;
  }

PaintingBinding.handleSystemMessage 優(yōu)先調(diào)用 super.handleSystemMessage ,也就是先調(diào)用 ServicesBinding.handleSystemMessage異步處理系統(tǒng)發(fā)送的內(nèi)存緊張信號(hào),接著異步處理系統(tǒng)字體變動(dòng)事件。

1.4 PaintingBinding

除了前面講的監(jiān)聽(tīng)系統(tǒng)字體變化事件,這里主要是在繪制熱身幀之前預(yù)熱Skia渲染引擎

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    
    // 初始化圖片緩存
    _imageCache = createImageCache();
    
    if (shaderWarmUp != null) {
      //第一幀繪制前的預(yù)熱工作
      shaderWarmUp.execute();
    }
  }

1.5 SemanticsBinding

渲染輔助類(lèi)綁定,主要負(fù)責(zé)關(guān)聯(lián)語(yǔ)義樹(shù)與Flutter Engine。Flutter維護(hù)了一個(gè) semantic tree(語(yǔ)義樹(shù)),頁(yè)面構(gòu)建的時(shí)候會(huì)根據(jù)各Widget的語(yǔ)義描述構(gòu)建一棵 semantic tree。如在Image組件中配置 semanticLabel 語(yǔ)義內(nèi)容,用戶在IOS/Android手機(jī)開(kāi)啟無(wú)障礙功能時(shí),觸摸到該 Image 時(shí)通過(guò)語(yǔ)義樹(shù)查找到對(duì)應(yīng)的語(yǔ)義描述交給Flutter Engine,實(shí)現(xiàn)讀屏等功能。

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _accessibilityFeatures = window.accessibilityFeatures;
  }

1.6 RendererBinding

渲染綁定,RendererBinding是render tree 與 Flutter engine的粘合劑,它持有了render tree的根節(jié)點(diǎn) renderView

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    
    // 初始化PipelineOwner管理渲染流程
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    
    // 設(shè)置window的屏幕參數(shù)變化、文本縮放因子變化、亮度等變化、語(yǔ)義啟用等回調(diào)。
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
      
    // 初始化一個(gè)RenderView作為render tree的根節(jié)點(diǎn),作為渲染執(zhí)行入口
    initRenderView();
    
    // 設(shè)置是否根據(jù)render tree生成語(yǔ)義樹(shù)
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    
    // 繪制回調(diào)
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    
    // 初始化鼠標(biāo)監(jiān)聽(tīng)
    initMouseTracker();
  }

GestureBinding.initInstances 方法中的事件處理,調(diào)用的就是這里的renderView.hitTest 從根節(jié)點(diǎn)開(kāi)始命中測(cè)試的。正因?yàn)?RenderBinding 創(chuàng)建并持有了 RenderView 實(shí)例,所以 GestureBinding 中通過(guò) mixin 機(jī)制將 RenderBinding 的 hitTest 方法混入,從而可以實(shí)現(xiàn)命中測(cè)試,相當(dāng)于需要用到命中測(cè)試的地方都通過(guò) mixin 委托給 RenderBinding 來(lái)實(shí)現(xiàn)了。

addPersistentFrameCallback 將繪制處理回調(diào)_handlePersistentFrameCallback 加入到 FrameCallback 類(lèi)型回調(diào)列表,_handlePersistentFrameCallback 中的 drawFrame 實(shí)現(xiàn)繪制流水線。

WidgetsBinding

組件綁定

@override
  void initInstances() {
    super.initInstances();
    _instance = this;

    assert(() {
      _debugAddStackFilters();
      return true;
    }());

    // 初始化BuildOwnder,處理需要繪制的Element的構(gòu)建工作
    _buildOwner = BuildOwner();
    
    // 通過(guò)SchedulerBinding初始化window的onBeginFrame、onDrawFrame回調(diào)
    // 如果app可見(jiàn),通過(guò)window.scheduleFrame向engine發(fā)起繪制請(qǐng)求
    buildOwner.onBuildScheduled = _handleBuildScheduled;
    
    // 語(yǔ)言環(huán)境變化處理
    window.onLocaleChanged = handleLocaleChanged;
    
    // platform訪問(wèn)權(quán)限變化處理
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    
    // 處理系統(tǒng)發(fā)送的push/pop頁(yè)面請(qǐng)求
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
  }

WidgetsBinding屬于最外層的 mixin,作為處理 Widget 相關(guān)事件的入口。在初始化過(guò)程中主要是生成了 BuildOwner 實(shí)例,以及window的onBeginFrame、onDrawFrame 回調(diào),后面渲染流程會(huì)用到。

BindingBase先通過(guò)按順序執(zhí)行7個(gè)mixin的initInstances方法,完成了相關(guān)初始化工作,以及兩個(gè)重要類(lèi)的實(shí)例化PipelineOwner、BuildOwner。

然后就是執(zhí)行了initServiceExtensions方法,實(shí)現(xiàn)了該方法的mixin按調(diào)用順序?yàn)閃idgetsBinding-->RendererBinding-->SchedulerBinding-->ServicesBinding主要就是在debug模式下注冊(cè)相關(guān)拓展服務(wù)。

2 綁定根節(jié)點(diǎn)(scheduleAttachRootWidget)

由于是組件相關(guān),scheduleAttachRootWidget 具體的實(shí)現(xiàn)在WidgetsBinding 里

  @protected
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      // 將傳入的Widget綁定到一個(gè)根節(jié)點(diǎn)并構(gòu)建三棵樹(shù)
      attachRootWidget(rootWidget);
    });
  }

將app的主widget(即傳入的 rootWidget)和根節(jié)點(diǎn)綁定。其中render tree 的根節(jié)點(diǎn)就是前面初始化流程中RendererBinding.initInstances過(guò)程創(chuàng)建的RenderView,RenderView是繼承自RenderObject的,所以還需要?jiǎng)?chuàng)建Element和Widget與之關(guān)聯(lián),而創(chuàng)建的 Element 和 Widget 分別對(duì)應(yīng)另外兩棵樹(shù)的根節(jié)點(diǎn)。

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

先是通過(guò)傳入的 rootWidget 及 RenderView 實(shí)例化了一個(gè)RenderObjectToWidgetAdapter對(duì)象,而RenderObjectToWidgetAdapter是繼承自RenderObjectWidget,即創(chuàng)建了Widget樹(shù)的根節(jié)點(diǎn)。繼續(xù)調(diào)用 attachToRenderTree

  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {
      
        // 創(chuàng)建了一個(gè)RenderObjectToWidgetElement實(shí)例作為element tree的根節(jié)點(diǎn)
        element = createElement();
        assert(element != null);
        
        // 綁定BuildOwner
        element.assignOwner(owner);
      });
      
      // 標(biāo)記需要構(gòu)建的element,并rebuild
      owner.buildScope(element, () {
        element.mount(null, null);
      });

      SchedulerBinding.instance.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }
@override
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

attachToRenderTree 中通過(guò) createElement() 創(chuàng)建了一個(gè)RenderObjectToWidgetElement 實(shí)例作為 element tree 的根節(jié)點(diǎn),并綁定BuildOwner,通過(guò) BuildOwner 構(gòu)建需要構(gòu)建的 element。

3 繪制熱身幀(scheduleWarmUpFrame)

綁定完根節(jié)點(diǎn)后,就開(kāi)始立即執(zhí)行scheduleWarmUpFrame()繪制熱身幀。不用繪制熱身幀也可以渲染的,那為什么還需要繪制熱身幀?

繪制熱身幀的目的是:

  1. 前面 window.scheduleFrame 發(fā)起繪制請(qǐng)求是在收到Vsync信號(hào)后才開(kāi)始的,app初始化時(shí)為了節(jié)省時(shí)間并未等待Vsync信號(hào)直接開(kāi)始繪制,最多可以節(jié)省16.6ms(60Hz屏幕刷新率)等待時(shí)間。
  2. 在熱身幀繪制結(jié)束前通過(guò)加鎖來(lái)屏蔽期間的屏幕指針事件處理及_taskQueue中的回調(diào),保證在繪制過(guò)程中不會(huì)再觸發(fā)新的重繪。
  3. 在熱身幀繪制結(jié)束后調(diào)用 resetEpoch() 來(lái)重置時(shí)間戳,避免熱重載情況從熱身幀到熱重載幀的時(shí)間差,導(dǎo)致隱式動(dòng)畫(huà)的跳幀情況。

和普通繪制一樣,熱身幀也是通過(guò)handleBeginFrame、handleDrawFrame這兩個(gè)回調(diào)來(lái)進(jìn)行繪制流程,在前面 WidgetBinding 初始化時(shí)將這兩個(gè)回調(diào)交給了window,具體代碼邏輯是在 SchedulerBinding。

void scheduleWarmUpFrame() {
    if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
      return;

    _warmUpFrame = true;
    Timeline.startSync('Warm-up frame');
    final bool hadScheduledFrame = _hasScheduledFrame;
    
    // Timer任務(wù)會(huì)加入到event queue
    // 所以在執(zhí)行繪制前先處理完microtask queue中的任務(wù)
    Timer.run(() {
      assert(_warmUpFrame);
      
      // 繪制Frame前工作,主要是處理Animate動(dòng)畫(huà)
      handleBeginFrame(null);
    });
    Timer.run(() {
      assert(_warmUpFrame);
      
      // 開(kāi)始Frame繪制
      handleDrawFrame();
      
      // 重置時(shí)間戳
      resetEpoch();
      _warmUpFrame = false;
      if (hadScheduledFrame)
      
        // 后續(xù)Frame繪制請(qǐng)求
        scheduleFrame();
    });

    lockEvents(() async {
      await endOfFrame;
      Timeline.finishSync();
    });
  }

handleBeginFrame處理動(dòng)畫(huà)相關(guān)邏輯,動(dòng)畫(huà)回調(diào)后并不立即執(zhí)行動(dòng)畫(huà),而是改變了animation.value,并調(diào)用setSate()來(lái)發(fā)起繪制請(qǐng)求。動(dòng)畫(huà)的過(guò)程就是在 Vsync 信號(hào)到來(lái)時(shí)根據(jù)動(dòng)畫(huà)進(jìn)度計(jì)算出對(duì)應(yīng)的 value,而對(duì)應(yīng)的 Widget 也會(huì)隨著 animation.value 的變化而重建,從而形成動(dòng)畫(huà),和Android的屬性動(dòng)畫(huà)原理差不多。
?
?handleBeginFrame處理完后,會(huì)優(yōu)先處理microTask任務(wù)隊(duì)列。然后才是event Task,window.onDrawFrame(),對(duì)應(yīng)SchedulerBinding.handleDrawFrame()。(Timer任務(wù)會(huì)加入到event queue,flutter的事件處理機(jī)制是優(yōu)先處理 micro queue 中任務(wù))

  void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    Timeline.finishSync(); // end the "Animate" phase
    try {
      // 處理Persistent類(lèi)型回調(diào),主要包括build\layout\draw流程
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (final FrameCallback callback in _persistentCallbacks)
        // 注釋1
        _invokeFrameCallback(callback, _currentFrameTimeStamp);

      // 處理Post-Frame回調(diào),主要是狀態(tài)清理,準(zhǔn)備調(diào)度下一幀繪制請(qǐng)求
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (final FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);
    } finally {
    
      // 處理完成,設(shè)置狀態(tài)為idle
      _schedulerPhase = SchedulerPhase.idle;
      Timeline.finishSync(); // end the Frame
      assert(() {
        if (debugPrintEndFrameBanner)
          debugPrint('?' * _debugBanner.length);
        _debugBanner = null;
        return true;
      }());
      _currentFrameTimeStamp = null;
    }
  }

上面代碼中的注釋1處的 _invokeFrameCallback 中處理的 callback 就是我們上面在 RendererBinding 中的 被添加進(jìn) FrameCallback 類(lèi)型的列表中的 _handlePersistentFrameCallback,_handlePersistentFrameCallback 里面調(diào)用了 drawFrame,這里也是用到了 mixin 機(jī)制,在 WidgetsBinding.drawFrame() 中完成組件的構(gòu)建任務(wù),在 RendererBinding.drawFrame 完成組件的布局、繪制任務(wù)

  // RendererBinding
  void _handlePersistentFrameCallback(Duration timeStamp) {
    drawFrame();
    _mouseTracker.schedulePostFrameCheck();
  }

  void drawFrame() {
    assert(renderView != null);
    // 布局
    pipelineOwner.flushLayout();
    // 更新 RenderObject 中需要繪制的內(nèi)容
    pipelineOwner.flushCompositingBits();
    // 繪制
    pipelineOwner.flushPaint();
    if (sendFramesToEngine) {
      // 產(chǎn)生這一幀的數(shù)據(jù)Scene,由window.render交給Engine,最終顯示到屏幕(發(fā)送數(shù)據(jù)到GPU)。
      renderView.compositeFrame();
      // 將語(yǔ)義樹(shù)發(fā)送到操作系統(tǒng)
      pipelineOwner.flushSemantics();
      _firstFrameSent = true;
    }
  }
// WidgetsBinding
void drawFrame() {
   ...
   
   try {
    if (renderViewElement != null)
      //調(diào)用BuildOwner.buildScope開(kāi)始構(gòu)建
      buildOwner.buildScope(renderViewElement);
      
    //調(diào)用RendererBinding.drawFrame,開(kāi)始布局、繪制階段。
    super.drawFrame();
    
    //從element tree中移除不需要的element,unmount
    buildOwner.finalizeTree();
  } finally {
     ...
  }
}

繪制流程結(jié)束后會(huì)調(diào)用renderView.compositeFrame()產(chǎn)生這一幀的數(shù)據(jù) Scene,由 window.render 交給Engine,最終顯示到屏幕。整個(gè)熱身幀繪制流程:

SchedulerBinding.scheduleWarmUpFrame
-> SchedulerBinding.handleBeginFrame 處理動(dòng)畫(huà)
-> SchedulerBinding.handleDrawFrame
-----> WidgetBinding.drawFrame 通過(guò) buildOwner 構(gòu)建組件
-----> RendererBinding.drawFrame 通過(guò) pipelineOwner 完成組件布局和繪制
-----> renderView.compositeFrame 發(fā)送 Scene 到GPU

總結(jié)

mixin機(jī)制在FlutterApp啟動(dòng)過(guò)程帶來(lái)的優(yōu)勢(shì):

  1. 高內(nèi)聚低耦合:適合應(yīng)用于需要多個(gè)功能模塊配合完成的場(chǎng)景,將功能模塊通過(guò)mixin解耦,各模塊職責(zé)單一,相互之間不直接引用。
  2. 代碼復(fù)用:多個(gè)類(lèi)可通過(guò)混入 mixin 類(lèi)來(lái)復(fù)用 mixin 類(lèi)的代碼
  3. 保證調(diào)用順序:mixin配合super調(diào)用,可以實(shí)現(xiàn)同名方法的“繼承鏈”式調(diào)用,保證串行執(zhí)行順序。

Flutter App的啟動(dòng)過(guò)程總結(jié):

  1. ensureInitialized 通過(guò)7個(gè) mixin 類(lèi) 按順序完成相關(guān)初始化工作
  2. scheduleAttachRootWidget 綁定app 應(yīng)用啟動(dòng)的 Widget 到 render tree 的根節(jié)點(diǎn)RenderView上并生成widget tree 的根節(jié)點(diǎn) RenderObjectToWidgetAdapter,RenderView又關(guān)聯(lián)了widget tree 的根節(jié)點(diǎn)和 element tree 的根節(jié)點(diǎn)
  3. scheduleWarmUpFrame 完成熱身幀繪制

參考文章:
從mixin機(jī)制理解Flutter App啟動(dòng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容