成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[415500]]

背景

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

Flutter 程序入口

我們編寫的 Flutter App 一般入口都是在 main 方法,其內部通過調用 runApp 方法將我們自己整個應用的 Widget 添加并運行,所以我們直接去看下 runApp 方法實現,如下:

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

可以看到上面三行代碼代表了 Flutter 啟動的核心三步(級聯運算符調用):

  1. WidgetsFlutterBinding 初始化(ensureInitialized())
  2. 綁定根節點創建核心三棵樹(scheduleAttachRootWidget(app))
  3. 繪制熱身幀(scheduleWarmUpFrame())

WidgetsFlutterBinding 實例及初始化

直接看源碼,如下:

  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 架構和 Flutter Engine 連接的核心橋梁,也是整個 Flutter 的應用層核心。通過 ensureInitialized() 方法我們可以得到一個全局單例的 WidgetsFlutterBinding 實例,且 mixin 的一堆 XxxBinding 也被實例化。

BindingBase 抽象類的構造方法中會調用initInstances()方法,而各種 mixin 的 XxxBinding 實例化重點也都在各自的initInstances()方法中,每個 XxxBinding 的職責不同,如下:

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

從 Flutter 架構宏觀抽象看,這些 XxxBinding 承擔的角色大致是一個橋梁關聯綁定,如下:

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

  1. mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { 
  2.   @override 
  3.   void initInstances() { 
  4.     ...... 
  5.     /** 
  6.      *1、創建一個管理Widgets的類對象 
  7.      *BuildOwner類用來跟蹤哪些Widget需要重建,并處理用于Widget樹的其他任務,例如管理不活躍的Widget等,調試模式觸發重建等。 
  8.      */ 
  9.     _buildOwner = BuildOwner(); 
  10.     //2、回調方法賦值,當第一個可構建元素被標記為臟時調用。 
  11.     buildOwner!.onBuildScheduled = _handleBuildScheduled; 
  12.     //3、回調方法賦值,當本地配置變化或者AccessibilityFeatures變化時調用。 
  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、創建管理rendering渲染管道的類 
  24.      * 提供接口調用用來觸發渲染。 
  25.      */ 
  26.     _pipelineOwner = PipelineOwner( 
  27.       onNeedVisualUpdate: ensureVisualUpdate, 
  28.       onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, 
  29.       onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, 
  30.     ); 
  31.     //5、一堆window變化相關的回調監聽 
  32.     window 
  33.       ..onMetricsChanged = handleMetricsChanged 
  34.       ..onTextScaleFactorChanged = handleTextScaleFactorChanged 
  35.       ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged 
  36.       ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged 
  37.       ..onSemanticsAction = _handleSemanticsAction; 
  38.     //6、創建RenderView對象,也就是RenderObject渲染樹的根節點 
  39.     initRenderView(); 
  40.     ...... 
  41.   } 
  42.  
  43.   void initRenderView() { 
  44.     ...... 
  45.     //RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> 
  46.     //7、渲染樹的根節點對象 
  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()中實例化賦值就等于給_pipelineOwner.rootNode也進行了賦值操作。 
  53.   set renderView(RenderView value) { 
  54.     assert(value != null); 
  55.     _pipelineOwner.rootNode = value; 
  56.   } 

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

通過 scheduleAttachRootWidget 創建關聯三棵核心樹

  1. mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { 
  2.   @protected 
  3.   void scheduleAttachRootWidget(Widget rootWidget) { 
  4.    //簡單的異步快速執行,將attachRootWidget異步化 
  5.     Timer.run(() { 
  6.       attachRootWidget(rootWidget); 
  7.     }); 
  8.   } 
  9.  
  10.   void attachRootWidget(Widget rootWidget) { 
  11.    //1、是不是啟動幀,即看renderViewElement是否有賦值,賦值時機為步驟2 
  12.     final bool isBootstrapFrame = renderViewElement == null
  13.     _readyToProduceFrames = true
  14.     //2、橋梁創建RenderObject、Element、Widget關系樹,_renderViewElement值為attachToRenderTree方法返回值 
  15.     _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( 
  16.       //3、RenderObjectWithChildMixin類型,繼承自RenderObject,RenderObject繼承自AbstractNode。 
  17.       //來自RendererBinding的_pipelineOwner.rootNode,_pipelineOwner來自其初始化initInstances方法實例化的PipelineOwner對象。 
  18.       //一個Flutter App全局只有一個PipelineOwner實例。 
  19.       container: renderView,  
  20.       debugShortDescription: '[root]'
  21.       //4、我們平時寫的dart Widget app 
  22.       child: rootWidget, 
  23.     //5、attach過程,buildOwner來自WidgetsBinding初始化時實例化的BuildOwner實例,renderViewElement值就是_renderViewElement自己,此時由于調用完appach才賦值,所以首次進來也是null。 
  24.     ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?); 
  25.     if (isBootstrapFrame) { 
  26.       //6、首幀主動更新一下,匹配條件的情況下內部本質是調用SchedulerBinding的scheduleFrame()方法。 
  27.       //進而本質調用了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函數參數中傳遞的Flutter應用Widget樹根 
  5.   final Widget? child; 
  6.   //4、繼承自RenderObject,來自PipelineOwner對象的rootNode屬性,一個Flutter App全局只有一個PipelineOwner實例。 
  7.   final RenderObjectWithChildMixin<T> container; 
  8.   ...... 
  9.   //5、重寫Widget的createElement實現,構建了一個RenderObjectToWidgetElement實例,它繼承于Element。         
  10.   //Element樹的根結點是RenderObjectToWidgetElement。 
  11.   @override 
  12.   RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this); 
  13.   //6、重寫Widget的createRenderObject實現,container本質是一個RenderView。 
  14.   //RenderObject樹的根結點是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實例創建后調用 
  23.    *owner來自WidgetsBinding初始化時實例化的BuildOwner實例,element 值就是自己。 
  24.    *該方法創建根Element(RenderObjectToWidgetElement),并將Element與Widget進行關聯,即創建WidgetTree對應的ElementTree。 
  25.    *如果Element已經創建過則將根Element中關聯的Widget設為新的(即_newWidget)。 
  26.    *可以看見Element只會創建一次,后面都是直接復用的。 
  27.    */ 
  28.   RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) { 
  29.     //8、由于首次實例化RenderObjectToWidgetAdapter調用attachToRenderTree后才不為null,所以當前流程為null 
  30.     if (element == null) { 
  31.       //9、在lockState里面代碼執行過程中禁止調用setState方法 
  32.       owner.lockState(() { 
  33.         //10、創建一個Element實例,即調用本段代碼片段中步驟5的方法。 
  34.         //調用RenderObjectToWidgetAdapter的createElement方法構建了一個RenderObjectToWidgetElement實例,繼承RootRenderObjectElement,又繼續繼承RenderObjectElement,接著繼承Element。 
  35.         element = createElement(); 
  36.         assert(element != null); 
  37.         //11、給根Element的owner屬性賦值為WidgetsBinding初始化時實例化的BuildOwner實例。 
  38.         element!.assignOwner(owner); 
  39.       }); 
  40.       //12、重點!mount里面RenderObject  
  41.       owner.buildScope(element!, () { 
  42.         element!.mount(nullnull); 
  43.       }); 
  44.     } else { 
  45.       //13、更新widget樹時_newWidget賦值為新的,然后element數根標記為markNeedsBuild 
  46.       element._newWidget = this; 
  47.       element.markNeedsBuild(); 
  48.     } 
  49.     return element!; 
  50.   } 
  51.   ...... 

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

  1. abstract class RenderObjectElement extends Element { 
  2.   //1、Element樹通過構造方法RenderObjectToWidgetElement持有了Widget樹實例。(RenderObjectToWidgetAdapter)。 
  3.   @override 
  4.   RenderObjectWidget get widget => super.widget as RenderObjectWidget; 
  5.  
  6.   //2、Element樹通過mount后持有了RenderObject渲染樹實例。 
  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)調用createRenderObject方法傳入Element實例自己獲取RenderObject渲染樹。 
  15.     //RenderObjectToWidgetAdapter.createRenderObject(this)返回的是RenderObjectToWidgetAdapter的container成員,也就是上面分析的RenderView渲染樹根節點。 
  16.     _renderObject = widget.createRenderObject(this); 
  17.     ...... 
  18.   } 

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

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

上面代碼流程對應的時序圖大致如下:

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

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

熱身幀繪制

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

  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.       //重置時間戳,避免熱重載情況從熱身幀到熱重載幀的時間差,導致隱式動畫的跳幀情況。 
  12.       resetEpoch(); 
  13.       ...... 
  14.       if (hadScheduledFrame) 
  15.         scheduleFrame(); 
  16.     }); 
  17.  //在此次繪制結束前該方法會鎖定事件分發,可保證繪制過程中不會再觸發新重繪。 
  18.  //也就是說在本次繪制結束前不會響應各種事件。 
  19.     lockEvents(() async { 
  20.       await endOfFrame; 
  21.       Timeline.finishSync(); 
  22.     }); 
  23.   } 

這段代碼的本質這里先不詳細展開,因為本質就是渲染幀的提交與觸發相關,我們后邊文章會詳細分析 framework 層繪制渲染相關邏輯,那時再展開。在這里只用知道它被調用后會立即執行一次繪制(不用等待 VSYNC 信號到來)。

這時候細心的話,你可能會有疑問,前面分析 attachRootWidget 方法調用時,它的最后一行發現是啟動幀則會調用window.scheduleFrame()然后等系統 VSYNC 信號到來觸發繪制,既然 VSYNC 信號到來時會觸發繪制,這個主動熱身幀豈不是可以不要?

是的,不要也是沒問題的,只是體驗不是很好,會導致初始化卡幀的效果。因為前面window.scheduleFrame()發起的繪制請求是在收到系統 VSYNC 信號后才真正執行,而 Flutter app 初始化時為了盡快呈現 UI 而沒有等待系統 VSYNC 信號到來就主動發起一針繪制(也被形象的叫做熱身幀),這樣最長可以減少一個 VSYNC 等待時間。

總結

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

本文轉載自微信公眾號「碼農每日一題」,可以通過以下二維碼關注。轉載本文請聯系碼農每日一題公眾號。

 

責任編輯:武曉燕 來源: 碼農每日一題
相關推薦

2010-07-14 11:00:51

2017-06-26 11:37:40

互聯網

2021-01-12 08:20:51

AndroidActivity系統

2023-01-04 15:24:46

ACE組件UI布局

2019-11-27 11:10:58

TomcatOverviewAcceptor

2017-11-21 13:00:20

機器學習決策樹可視化

2012-07-16 10:19:02

MongoDB

2018-02-25 07:23:23

2017-11-09 10:28:45

軟件定義網絡

2011-08-01 13:51:31

Web

2022-10-09 15:18:31

SwaggerOpenAPI工具

2021-01-19 05:46:00

算法javascript函數

2017-08-22 16:25:14

CSSHTML選擇器

2021-09-06 10:38:50

二叉搜索樹遞歸

2021-06-04 07:55:05

MySQLB+ 樹數據

2015-06-15 18:44:15

Apple Watch微游戲

2021-01-14 18:17:33

SpringFrameIOCJava

2021-12-08 15:10:45

鴻蒙HarmonyOS應用

2020-07-01 07:44:06

javaSE==equals

2009-07-08 16:00:57

J2SE 1.2Java2
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本a在线 | 久热电影 | 久久国产精品-久久精品 | 精品日韩在线 | 99精品视频网 | 久久精品高清视频 | 精品久久久久久久久久久 | 日本涩涩网 | 天堂中文字幕av | 欧美日韩成人一区二区 | 久久亚洲一区二区 | 欧美日韩中文字幕在线 | 久久精品视频网站 | 免费一二区 | 99精品一级欧美片免费播放 | 日韩在线播放网址 | 黄色毛片免费 | 大象视频一区二区 | 欧美日本韩国一区二区三区 | 草久免费视频 | 亚洲一级毛片 | 啪啪毛片 | 日韩www | 中文精品久久 | 久久国产精品免费一区二区三区 | 成人毛片视频免费 | 精品视频在线播放 | 欧美情趣视频 | 天天久| 欧美一区二区三区大片 | 日韩羞羞 | 精品国产乱码久久久久久丨区2区 | 国产日韩在线观看一区 | 久久久免费电影 | 欧洲一级视频 | 中文字幕第一页在线 | 韩国av电影网 | 中文字幕1区 | 欧美日韩国产一区二区三区 | 国产精品99久久久久久www | 免费同性女女aaa免费网站 |