這是我第三篇Flutter相關博客 歡迎 查看個人前兩篇 Flutter實現篇html
Flutter是Google用以幫助開發者在Ios和Android兩個平臺開發高質量原生應用的全新移動UI框架.我開始認識Flutter時,經歷了三個Flutter重要歷史版本.前端
此後愈來愈多的人開始關注到Flutter。web
在 Flutter 誕生以前,已經有許多跨平臺 UI 框架的方案,好比基於 WebView 的 Cordova、AppCan 等,還有使用 HTML+JavaScript 渲染成原生控件的 React Native、Weex 等。Flutter 則開闢了一種全新的思路,從頭到 尾重寫一套跨平臺的 UI 框架,包括 UI 控件、渲染邏輯甚至開發語言。算法
從圖中能夠看出 Flutter主要被分爲兩層 Framework層和Flutter Engine.網絡
Framework層所有使用Dart編寫,有完整UI框架的API,並預寫了Android(MaterialDesign)和IOS的(Cupertino)風格的UI,極大方便了開發移動端.數據結構
Framework 底層是 Flutter 引擎, 引擎主要負責圖形繪製 (Skia)、 文字排版 (libtxt) 和提供 Dart 運行時, 引擎所有使用 C++實現.多線程
在看Flutter框架前,咱們先看一下其餘跨平臺框架的設計架構
咱們知道Flutter的Framework層是使用了Dart語言編寫,那Dart語言有哪些優點呢?下面分爲幾個點來闡述框架
DartVM的內存分配策略很是簡單,建立對象時只須要在現有堆上移動指針,內存增加始終是線形的,省去了查找可用內存段的過程異步
Dart中相似線程的概念叫作Isolate,每一個Isolate之間是沒法共享內存的,因此這種分配策略可讓Dart實現無鎖的快速分配。
Dart的垃圾回收也採用了多生代算法,新生代在回收內存時採用了「半空間」算法,觸發垃圾回收時Dart會將當前半空間中的「活躍」對象拷貝到備用空間,而後總體釋放當前空間的全部內存如圖.
整個過程當中Dart只須要操做少許的「活躍」對象,大量的沒有引用的「死亡」對象則被忽略,這種 多生代無鎖垃圾回收器,專門爲UI框架中常見的大量Widgets對象建立和銷燬優化,很是適合Flutter框架中大量Widget重建的場景.
代碼體積優化(Tree Shaking),編譯時只保留運行時須要調用的代碼(不容許反射這樣的隱式引用),因此龐大的Widgets庫不會形成發佈體積過大。
Dart支持兩種編譯模式:
在debug模式下使用JIT編譯,生成srcipt/bytecode進行解釋執行,能夠支持HotReload(熱重載),修改代碼,保持便可在設備上看到效果. 而在Release下 AOT編譯生成Machine Code,高效的運行.
對於移動端的交互來講,大多數狀況下都是在等待狀態,等待網絡請求,等待用戶輸入等.那麼設想一下,發起一個網絡請求只在一個線程中能夠進行嗎?固然網絡請求確定是異步的(注意這裏說的異步而多線程並不是一個概念.),事實驗證是能夠的,Flutter就採用了Dart這種單線程機制,省去了多線程上下文切換帶來的性能損耗.(對於高耗時操做,也一樣支持多線程操做,經過Isolate開啓,不過注意這裏的多線程,內存是沒法共享的.)
當一個Dart的方法開始執行時,他會一直執行直至達到這個方法的退出點。換句話說Dart的方法是不會被其餘Dart代碼打斷的。 當一個Dart應用開始的標誌是它的main isolate執行了main方法。當main方法退出後,main isolate的線程就會去逐一處理消息隊列中的消息。
有了消息隊列,而後有了循環去讀取消息隊列中的消息,就能夠有單線程去執行異步消息的能力. 通常的消息使用dart:async中使用Future來支持異步消息.
在講Flutter Engin層時,咱們先講一下屏幕繪製的原理.
咱們都知道顯示器以固定的頻率刷新,好比 iPhone的 60Hz、iPad Pro的 120Hz。當一幀圖像繪製完畢後準備繪製下一幀時,顯示器會發出一個垂直同步信號(VSync),因此 60Hz的屏幕就會一秒內發出 60次這樣的信號。
而且通常地來講,計算機系統中,CPU、GPU和顯示器以一種特定的方式協做:CPU將計算好的顯示內容提交給 GPU,GPU渲染後放入幀緩衝區,而後視頻控制器按照 VSync信號從幀緩衝區取幀數據傳遞給顯示器顯示。
做爲一個專職Android開發,看過Android的繪圖機制,經過SurfaceFlinger 和HAL層之間的工做機制發現和Flutter的很像,那麼IOS的如何呢?我的推測屏幕的繪圖機制是同樣的,只是不一樣平臺有不一樣實現.
Flutter只關心向 GPU提供視圖數據,GPU的 VSync信號同步到 UI線程,UI線程使用 Dart來構建抽象的視圖結構,這份數據結構在 GPU線程進行圖層合成,視圖數據提供給 Skia引擎渲染爲 GPU數據,這些數據經過 OpenGL或者 Vulkan提供給 GPU.
因此 Flutter並不關心顯示器、視頻控制器以及 GPU具體工做,它只關心 GPU發出的 VSync信號,儘量快地在兩個 VSync信號之間計算併合成視圖數據,而且把數據提供給 GPU.
在 Flutter 界面渲染過程分爲 3 個階段: 佈局、繪製、合成.
而佈局階段,有三個重要的對象.RenderObject、Element、Widget.
Widget是開發常常接觸的控件,默認是隻讀的.
Element 是 Flutter 用來分離控件樹和真正的渲染 對象的中間層, 控件用來描述對應的 element 屬性,控件重建後可能會複用同一個 element.
RenderObject 負責提供配置信息並建立具體的 Element。
Element 持有真正負責佈局、 繪製和碰撞測試 (hit test) 的 RenderObject 對象.
那麼這樣,若是控件的屬性發生了變化 (由於控件的屬性是隻 讀的, 因此變化也就意味着從新建立了新的控件樹), 可是其樹上每一個節點的類型沒有變化時, element 樹和 render 樹能夠徹底重用原來的對象 (由於 element 和 render object 的屬性都是可變的)
傳統佈局,如Android可能須要屢次Measure,計算寬高。Flutter 採用約束進行單次測量佈局. 整個佈局過程當中只須要深度遍歷一次,極大的提高效能。
渲染對象樹中的每一個對象都會在佈局過程當中接受父 對象的 Constraints 參數,決定本身的大小, 而後父對象 就能夠按照本身的邏輯決定各個子對象的位置,完成佈局過程.
子對象不存儲本身在容器中的位置, 因此在它的位置發生改變時並不須要從新佈局或者繪製. 子對象的位 置信息存儲在它本身的 parentData 字段中,可是該字段由它的父對象負責維護,自身並不關心該字段的內容。
同時也由於這種簡單的佈局邏輯, Flutter 能夠在某些節 點設置佈局邊界 (Relayout boundary), 即當邊界內的任 何對象發生從新佈局時, 不會影響邊界外的對象, 反之亦然.
文中參考的資料以下