自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

談?wù)?Flutter 的 RunApp 與三棵樹誕生流程?

開發(fā) 前端
從寫 Flutter 第一行程序開始我們就知道在 Dart 的 main 方法中通過調(diào)用 runApp 方法把自己編寫的 Widget 傳遞進(jìn)去,只有這樣編譯運(yùn)行后才能得到預(yù)期效果。

[[415500]]

背景

從寫 Flutter 第一行程序開始我們就知道在 Dart 的 main 方法中通過調(diào)用 runApp 方法把自己編寫的 Widget 傳遞進(jìn)去,只有這樣編譯運(yùn)行后才能得到預(yù)期效果。你有沒有好奇這背后都經(jīng)歷了什么?runApp 為什么這么神秘?或者說,在你入門 Flutter 后應(yīng)該經(jīng)常聽到或看到過 Flutter 三棵樹核心機(jī)制的東西,你有真正的想過他們都是什么嗎?如果都沒有,那么本文就是一場解密之旅。

Flutter 程序入口

我們編寫的 Flutter App 一般入口都是在 main 方法,其內(nèi)部通過調(diào)用 runApp 方法將我們自己整個(gè)應(yīng)用的 Widget 添加并運(yùn)行,所以我們直接去看下 runApp 方法實(shí)現(xiàn),如下:

  1. /** 
  2.  * 位置:FLUTTER_SDK\packages\flutter\lib\src\widgets\binding.dart 
  3.  * 注意:app參數(shù)的Widget布局盒子約束constraints會被強(qiáng)制為填充屏幕,這是框架機(jī)制,自己想要調(diào)整可以用Align等包裹。 
  4.  * 多次重復(fù)調(diào)用runApp將會從屏幕上移除已添加的app Widget并添加新的上去, 
  5.  * 框架會對新的Widget樹與之前的Widget樹進(jìn)行比較,并將任何差異應(yīng)用于底層渲染樹,有點(diǎn)類似于StatefulWidget 
  6. 調(diào)用State.setState后的重建機(jī)制。 
  7.  */ 
  8. void runApp(Widget app) { 
  9.   WidgetsFlutterBinding.ensureInitialized() 
  10.     ..scheduleAttachRootWidget(app) 
  11.     ..scheduleWarmUpFrame(); 

可以看到上面三行代碼代表了 Flutter 啟動的核心三步(級聯(lián)運(yùn)算符調(diào)用):

  1. WidgetsFlutterBinding 初始化(ensureInitialized())
  2. 綁定根節(jié)點(diǎn)創(chuàng)建核心三棵樹(scheduleAttachRootWidget(app))
  3. 繪制熱身幀(scheduleWarmUpFrame())

WidgetsFlutterBinding 實(shí)例及初始化

直接看源碼,如下:

  1. class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { 
  2.   static WidgetsBinding ensureInitialized() { 
  3.     if (WidgetsBinding.instance == null
  4.       WidgetsFlutterBinding(); 
  5.     return WidgetsBinding.instance!; 
  6.   } 

WidgetsFlutterBinding 繼承自 BindingBase,并且 with 了大量的 mixin 類。WidgetsFlutterBinding 就是將 Widget 架構(gòu)和 Flutter Engine 連接的核心橋梁,也是整個(gè) Flutter 的應(yīng)用層核心。通過 ensureInitialized() 方法我們可以得到一個(gè)全局單例的 WidgetsFlutterBinding 實(shí)例,且 mixin 的一堆 XxxBinding 也被實(shí)例化。

BindingBase 抽象類的構(gòu)造方法中會調(diào)用initInstances()方法,而各種 mixin 的 XxxBinding 實(shí)例化重點(diǎn)也都在各自的initInstances()方法中,每個(gè) XxxBinding 的職責(zé)不同,如下:

  • WidgetsFlutterBinding:核心橋梁主體,F(xiàn)lutter app 全局唯一。
  • BindingBase:綁定服務(wù)抽象類。
  • GestureBinding:Flutter 手勢事件綁定,處理屏幕事件分發(fā)及事件回調(diào)處理,其初始化方法中重點(diǎn)就是把事件處理回調(diào)_handlePointerDataPacket函數(shù)賦值給 window 的屬性,以便 window 收到屏幕事件后調(diào)用,window 實(shí)例是 Framework 層與 Engine 層處理屏幕事件的橋梁。
  • SchedulerBinding:Flutter 繪制調(diào)度器相關(guān)綁定類,debug 編譯模式時(shí)統(tǒng)計(jì)繪制流程時(shí)長等操作。
  • ServicesBinding:Flutter 系統(tǒng)平臺消息監(jiān)聽綁定類。即 Platform 與 Flutter 層通信相關(guān)服務(wù),同時(shí)注冊監(jiān)聽了應(yīng)用的生命周期回調(diào)。
  • PaintingBinding:Flutter 繪制預(yù)熱緩存等綁定類。
  • SemanticsBinding:語義樹和 Flutter 引擎之間的粘合劑綁定類。
  • RendererBinding:渲染樹和 Flutter 引擎之間的粘合劑綁定類,內(nèi)部重點(diǎn)是持有了渲染樹的根節(jié)點(diǎn)。
  • WidgetsBinding:Widget 樹和 Flutter 引擎之間的粘合劑綁定類。

從 Flutter 架構(gòu)宏觀抽象看,這些 XxxBinding 承擔(dān)的角色大致是一個(gè)橋梁關(guān)聯(lián)綁定,如下:

本文由于是啟動主流程相關(guān)機(jī)制分析,所以初始化中我們需要關(guān)注的主要是 RendererBinding 和 WidgetsBinding 類的initInstances()方法,如下:

  1. mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { 
  2.   @override 
  3.   void initInstances() { 
  4.     ...... 
  5.     /** 
  6.      *1、創(chuàng)建一個(gè)管理Widgets的類對象 
  7.      *BuildOwner類用來跟蹤哪些Widget需要重建,并處理用于Widget樹的其他任務(wù),例如管理不活躍的Widget等,調(diào)試模式觸發(fā)重建等。 
  8.      */ 
  9.     _buildOwner = BuildOwner(); 
  10.     //2、回調(diào)方法賦值,當(dāng)?shù)谝粋€(gè)可構(gòu)建元素被標(biāo)記為臟時(shí)調(diào)用。 
  11.     buildOwner!.onBuildScheduled = _handleBuildScheduled; 
  12.     //3、回調(diào)方法賦值,當(dāng)本地配置變化或者AccessibilityFeatures變化時(shí)調(diào)用。 
  13.     window.onLocaleChanged = handleLocaleChanged; 
  14.     window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged; 
  15.     ...... 
  16.   } 
  17.  
  18. mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { 
  19.   @override 
  20.   void initInstances() { 
  21.     ...... 
  22.     /** 
  23.      * 4、創(chuàng)建管理rendering渲染管道的類 
  24.      * 提供接口調(diào)用用來觸發(fā)渲染。 
  25.      */ 
  26.     _pipelineOwner = PipelineOwner( 
  27.       onNeedVisualUpdate: ensureVisualUpdate, 
  28.       onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, 
  29.       onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, 
  30.     ); 
  31.     //5、一堆window變化相關(guān)的回調(diào)監(jiān)聽 
  32.     window 
  33.       ..onMetricsChanged = handleMetricsChanged 
  34.       ..onTextScaleFactorChanged = handleTextScaleFactorChanged 
  35.       ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged 
  36.       ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged 
  37.       ..onSemanticsAction = _handleSemanticsAction; 
  38.     //6、創(chuàng)建RenderView對象,也就是RenderObject渲染樹的根節(jié)點(diǎn) 
  39.     initRenderView(); 
  40.     ...... 
  41.   } 
  42.  
  43.   void initRenderView() { 
  44.     ...... 
  45.     //RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> 
  46.     //7、渲染樹的根節(jié)點(diǎn)對象 
  47.     renderView = RenderView(configuration: createViewConfiguration(), window: window); 
  48.     renderView.prepareInitialFrame(); 
  49.   } 
  50.   //定義renderView的get方法,獲取自_pipelineOwner.rootNode 
  51.   RenderView get renderView => _pipelineOwner.rootNode! as RenderView; 
  52.   //定義renderView的set方法,上面initRenderView()中實(shí)例化賦值就等于給_pipelineOwner.rootNode也進(jìn)行了賦值操作。 
  53.   set renderView(RenderView value) { 
  54.     assert(value != null); 
  55.     _pipelineOwner.rootNode = value; 
  56.   } 

到此基于初始化過程我們已經(jīng)得到了一些重要信息,請記住 RendererBinding 中的 RenderView 就是 RenderObject 渲染樹的根節(jié)點(diǎn)。上面這部分代碼的時(shí)序圖大致如下:

通過 scheduleAttachRootWidget 創(chuàng)建關(guān)聯(lián)三棵核心樹

  1. mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { 
  2.   @protected 
  3.   void scheduleAttachRootWidget(Widget rootWidget) { 
  4.    //簡單的異步快速執(zhí)行,將attachRootWidget異步化 
  5.     Timer.run(() { 
  6.       attachRootWidget(rootWidget); 
  7.     }); 
  8.   } 
  9.  
  10.   void attachRootWidget(Widget rootWidget) { 
  11.    //1、是不是啟動幀,即看renderViewElement是否有賦值,賦值時(shí)機(jī)為步驟2 
  12.     final bool isBootstrapFrame = renderViewElement == null
  13.     _readyToProduceFrames = true
  14.     //2、橋梁創(chuàng)建RenderObject、Element、Widget關(guān)系樹,_renderViewElement值為attachToRenderTree方法返回值 
  15.     _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( 
  16.       //3、RenderObjectWithChildMixin類型,繼承自RenderObject,RenderObject繼承自AbstractNode。 
  17.       //來自RendererBinding的_pipelineOwner.rootNode,_pipelineOwner來自其初始化initInstances方法實(shí)例化的PipelineOwner對象。 
  18.       //一個(gè)Flutter App全局只有一個(gè)PipelineOwner實(shí)例。 
  19.       container: renderView,  
  20.       debugShortDescription: '[root]'
  21.       //4、我們平時(shí)寫的dart Widget app 
  22.       child: rootWidget, 
  23.     //5、attach過程,buildOwner來自WidgetsBinding初始化時(shí)實(shí)例化的BuildOwner實(shí)例,renderViewElement值就是_renderViewElement自己,此時(shí)由于調(diào)用完appach才賦值,所以首次進(jìn)來也是null。 
  24.     ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?); 
  25.     if (isBootstrapFrame) { 
  26.       //6、首幀主動更新一下,匹配條件的情況下內(nèi)部本質(zhì)是調(diào)用SchedulerBinding的scheduleFrame()方法。 
  27.       //進(jìn)而本質(zhì)調(diào)用了window.scheduleFrame()方法。 
  28.       SchedulerBinding.instance!.ensureVisualUpdate(); 
  29.     } 
  30.   } 

上面代碼片段的步驟 2 和步驟 5 需要配合 RenderObjectToWidgetAdapter 類片段查看,如下:

  1. //1、RenderObjectToWidgetAdapter繼承自RenderObjectWidget,RenderObjectWidget繼承自Widget 
  2. class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget { 
  3.   ...... 
  4.   //3、我們編寫dart的runApp函數(shù)參數(shù)中傳遞的Flutter應(yīng)用Widget樹根 
  5.   final Widget? child; 
  6.   //4、繼承自RenderObject,來自PipelineOwner對象的rootNode屬性,一個(gè)Flutter App全局只有一個(gè)PipelineOwner實(shí)例。 
  7.   final RenderObjectWithChildMixin<T> container; 
  8.   ...... 
  9.   //5、重寫Widget的createElement實(shí)現(xiàn),構(gòu)建了一個(gè)RenderObjectToWidgetElement實(shí)例,它繼承于Element。         
  10.   //Element樹的根結(jié)點(diǎn)是RenderObjectToWidgetElement。 
  11.   @override 
  12.   RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this); 
  13.   //6、重寫Widget的createRenderObject實(shí)現(xiàn),container本質(zhì)是一個(gè)RenderView。 
  14.   //RenderObject樹的根結(jié)點(diǎn)是RenderView。 
  15.   @override 
  16.   RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container; 
  17.  
  18.   @override 
  19.   void updateRenderObject(BuildContext context, RenderObject renderObject) { } 
  20.  
  21.   /** 
  22.    *7、上面代碼片段中RenderObjectToWidgetAdapter實(shí)例創(chuàng)建后調(diào)用 
  23.    *owner來自WidgetsBinding初始化時(shí)實(shí)例化的BuildOwner實(shí)例,element 值就是自己。 
  24.    *該方法創(chuàng)建根Element(RenderObjectToWidgetElement),并將Element與Widget進(jìn)行關(guān)聯(lián),即創(chuàng)建WidgetTree對應(yīng)的ElementTree。 
  25.    *如果Element已經(jīng)創(chuàng)建過則將根Element中關(guān)聯(lián)的Widget設(shè)為新的(即_newWidget)。 
  26.    *可以看見Element只會創(chuàng)建一次,后面都是直接復(fù)用的。 
  27.    */ 
  28.   RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) { 
  29.     //8、由于首次實(shí)例化RenderObjectToWidgetAdapter調(diào)用attachToRenderTree后才不為null,所以當(dāng)前流程為null 
  30.     if (element == null) { 
  31.       //9、在lockState里面代碼執(zhí)行過程中禁止調(diào)用setState方法 
  32.       owner.lockState(() { 
  33.         //10、創(chuàng)建一個(gè)Element實(shí)例,即調(diào)用本段代碼片段中步驟5的方法。 
  34.         //調(diào)用RenderObjectToWidgetAdapter的createElement方法構(gòu)建了一個(gè)RenderObjectToWidgetElement實(shí)例,繼承RootRenderObjectElement,又繼續(xù)繼承RenderObjectElement,接著繼承Element。 
  35.         element = createElement(); 
  36.         assert(element != null); 
  37.         //11、給根Element的owner屬性賦值為WidgetsBinding初始化時(shí)實(shí)例化的BuildOwner實(shí)例。 
  38.         element!.assignOwner(owner); 
  39.       }); 
  40.       //12、重點(diǎn)!mount里面RenderObject  
  41.       owner.buildScope(element!, () { 
  42.         element!.mount(nullnull); 
  43.       }); 
  44.     } else { 
  45.       //13、更新widget樹時(shí)_newWidget賦值為新的,然后element數(shù)根標(biāo)記為markNeedsBuild 
  46.       element._newWidget = this; 
  47.       element.markNeedsBuild(); 
  48.     } 
  49.     return element!; 
  50.   } 
  51.   ...... 

對于上面步驟 12 我們先進(jìn)去簡單看下 Element (RenderObjectToWidgetElement extends RootRenderObjectElement extends RenderObjectElement extends Element)的 mount 方法,重點(diǎn)關(guān)注的是父類 RenderObjectElement 中的 mount 方法,如下:

  1. abstract class RenderObjectElement extends Element { 
  2.   //1、Element樹通過構(gòu)造方法RenderObjectToWidgetElement持有了Widget樹實(shí)例。(RenderObjectToWidgetAdapter)。 
  3.   @override 
  4.   RenderObjectWidget get widget => super.widget as RenderObjectWidget; 
  5.  
  6.   //2、Element樹通過mount后持有了RenderObject渲染樹實(shí)例。 
  7.   @override 
  8.   RenderObject get renderObject => _renderObject!; 
  9.   RenderObject? _renderObject; 
  10.  
  11.   @override 
  12.   void mount(Element? parent, Object? newSlot) { 
  13.     ...... 
  14.     //3、通過widget樹(即RenderObjectToWidgetAdapter)調(diào)用createRenderObject方法傳入Element實(shí)例自己獲取RenderObject渲染樹。 
  15.     //RenderObjectToWidgetAdapter.createRenderObject(this)返回的是RenderObjectToWidgetAdapter的container成員,也就是上面分析的RenderView渲染樹根節(jié)點(diǎn)。 
  16.     _renderObject = widget.createRenderObject(this); 
  17.     ...... 
  18.   } 

到這里對于 Flutter 的靈魂“三棵樹”來說也能得出如下結(jié)論:

  • Widget 樹的根結(jié)點(diǎn)是 RenderObjectToWidgetAdapter(繼承自 RenderObjectWidget extends Widget),我們 runApp 中傳遞的 Widget 樹就被追加到了這個(gè)樹根的 child 屬性上。
  • Element 樹的根結(jié)點(diǎn)是 RenderObjectToWidgetElement(繼承自 RootRenderObjectElement extends RenderObjectElement extends Element),通過調(diào)用 RenderObjectToWidgetAdapter 的 createElement 方法創(chuàng)建,創(chuàng)建 RenderObjectToWidgetElement 的時(shí)候把 RenderObjectToWidgetAdapter 通過構(gòu)造參數(shù)傳遞進(jìn)去,所以 Element 的 _widget 屬性值為 RenderObjectToWidgetAdapter 實(shí)例,也就是說 Element 樹中 _widget 屬性持有了 Widget 樹實(shí)例。RenderObjectToWidgetAdapter 。
  • RenderObject 樹的根結(jié)點(diǎn)是 RenderView(RenderView extends RenderObject with RenderObjectWithChildMixin),在 Element 進(jìn)行 mount 時(shí)通過調(diào)用 Widget 樹(RenderObjectToWidgetAdapter)的createRenderObject方法獲取 RenderObjectToWidgetAdapter 構(gòu)造實(shí)例化時(shí)傳入的 RenderView 渲染樹根節(jié)點(diǎn)。

上面代碼流程對應(yīng)的時(shí)序圖大致如下:

結(jié)合上一小結(jié)可以很容易看出來三棵樹的創(chuàng)建時(shí)機(jī)(時(shí)序圖中紫紅色節(jié)點(diǎn)),也可以很容易看出來 Element 是 Widget 和 RenderObject 之前的一個(gè)“橋梁”,其內(nèi)部持有了兩者樹根,抽象表示如下:

由于篇幅和本文主題原因,我們重心關(guān)注三棵樹的誕生流程,對于三棵樹之間如何配合進(jìn)行繪制渲染這里先不展開,后面會專門一篇分析。

熱身幀繪制

到此讓我們先將目光再回到一開始runApp方法的實(shí)現(xiàn)中,我們還差整個(gè)方法實(shí)現(xiàn)中的最后一個(gè)scheduleWarmUpFrame()調(diào)用,如下:

  1. mixin SchedulerBinding on BindingBase { 
  2.   void scheduleWarmUpFrame() { 
  3.     ...... 
  4.     Timer.run(() { 
  5.       assert(_warmUpFrame); 
  6.       handleBeginFrame(null); 
  7.     }); 
  8.     Timer.run(() { 
  9.       assert(_warmUpFrame); 
  10.       handleDrawFrame(); 
  11.       //重置時(shí)間戳,避免熱重載情況從熱身幀到熱重載幀的時(shí)間差,導(dǎo)致隱式動畫的跳幀情況。 
  12.       resetEpoch(); 
  13.       ...... 
  14.       if (hadScheduledFrame) 
  15.         scheduleFrame(); 
  16.     }); 
  17.  //在此次繪制結(jié)束前該方法會鎖定事件分發(fā),可保證繪制過程中不會再觸發(fā)新重繪。 
  18.  //也就是說在本次繪制結(jié)束前不會響應(yīng)各種事件。 
  19.     lockEvents(() async { 
  20.       await endOfFrame; 
  21.       Timeline.finishSync(); 
  22.     }); 
  23.   } 

這段代碼的本質(zhì)這里先不詳細(xì)展開,因?yàn)楸举|(zhì)就是渲染幀的提交與觸發(fā)相關(guān),我們后邊文章會詳細(xì)分析 framework 層繪制渲染相關(guān)邏輯,那時(shí)再展開。在這里只用知道它被調(diào)用后會立即執(zhí)行一次繪制(不用等待 VSYNC 信號到來)。

這時(shí)候細(xì)心的話,你可能會有疑問,前面分析 attachRootWidget 方法調(diào)用時(shí),它的最后一行發(fā)現(xiàn)是啟動幀則會調(diào)用window.scheduleFrame()然后等系統(tǒng) VSYNC 信號到來觸發(fā)繪制,既然 VSYNC 信號到來時(shí)會觸發(fā)繪制,這個(gè)主動熱身幀豈不是可以不要?

是的,不要也是沒問題的,只是體驗(yàn)不是很好,會導(dǎo)致初始化卡幀的效果。因?yàn)榍懊鎤indow.scheduleFrame()發(fā)起的繪制請求是在收到系統(tǒng) VSYNC 信號后才真正執(zhí)行,而 Flutter app 初始化時(shí)為了盡快呈現(xiàn) UI 而沒有等待系統(tǒng) VSYNC 信號到來就主動發(fā)起一針繪制(也被形象的叫做熱身幀),這樣最長可以減少一個(gè) VSYNC 等待時(shí)間。

總結(jié)

上面就是 Flutter Dart 端三棵樹的誕生流程,關(guān)于三棵樹是如何互相工作的,我們會在后面專門篇章做分析,這里就先不展開了。

本文轉(zhuǎn)載自微信公眾號「碼農(nóng)每日一題」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系碼農(nóng)每日一題公眾號。

 

責(zé)任編輯:武曉燕 來源: 碼農(nóng)每日一題
相關(guān)推薦

2010-07-14 11:00:51

2017-06-26 11:37:40

互聯(lián)網(wǎng)

2021-01-12 08:20:51

AndroidActivity系統(tǒng)

2023-01-04 15:24:46

ACE組件UI布局

2019-11-27 11:10:58

TomcatOverviewAcceptor

2017-11-21 13:00:20

機(jī)器學(xué)習(xí)決策樹可視化

2011-08-01 13:51:31

Web

2012-07-16 10:19:02

MongoDB

2018-02-25 07:23:23

2017-11-09 10:28:45

軟件定義網(wǎng)絡(luò)

2022-10-09 15:18:31

SwaggerOpenAPI工具

2021-01-19 05:46:00

算法javascript函數(shù)

2021-09-06 10:38:50

二叉搜索樹遞歸

2021-06-04 07:55:05

MySQLB+ 樹數(shù)據(jù)

2017-08-22 16:25:14

CSSHTML選擇器

2021-12-08 15:10:45

鴻蒙HarmonyOS應(yīng)用

2021-01-14 18:17:33

SpringFrameIOCJava

2015-06-15 18:44:15

Apple Watch微游戲

2020-07-01 07:44:06

javaSE==equals

2009-07-08 16:00:57

J2SE 1.2Java2
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號