Flutter原理深度解析

九天星辰發表於2019-09-06

Google 出品,Dart語言,Flutter Engine引擎,響應式設計模式,原生渲染。

Flutter 是谷歌2018年釋出的跨平臺移動UI框架。與 react native 通過 Javascript 開發不同,Flutter 的程式語言是Dart,所以執行時並不需要 Javascript 引擎,但實際效果最終也通過原生渲染。

圖形效能媲美原生應用

其他框架只是做了OEM封裝,Flutter可以直接操作Skia進行繪製。

Flutter原理深度解析

跨平臺: 移動、Web、桌面、嵌入

Flutter原理深度解析

框架結構

Flutter原理深度解析

從該架構圖可知,Flutter框架可分為Framework層和Engine層;

框架(Framework)部分是用Dart語言寫的,也是本系列文章主要涉及的部分。

引擎(Engine)部分是用C++實現的。引擎為框架提供支撐,也是連線框架和系統(Android/iOS)的橋樑。

Flutter Framework: 整個框架層都是用Dart語言實現,該層提供一套基礎庫, 用於處理動畫、繪圖和手勢等。並且基於繪圖封裝了一套 UI元件庫,並且細分為兩種風格的元件 。

Foundation、Animation、Painting、Gestures 為 Dart 實現的 UI 層,提供動畫、手勢及繪製。

Rendering 渲染層,依賴 UI 層,在執行時 Rendering 層會構建一個 Widget 樹,當有變化時,會根據一定的演算法計算出有變化的部分,然後更新 Widget 樹。

Widgets 層是 Flutter 提供的的一套基礎元件庫,在基礎元件庫之上,Flutter 還提供了 Material 和 Cupertino 兩種視覺風格的元件庫。

Materail : Android風格的Widget

Cupertino: IOS風格的Widget

Flutter Engine

Skia 是一個開源的二維圖形庫,提供各種常用的 API,並可在多種軟硬體平臺上執行。谷歌 Chrome 瀏覽器、Chrome OS、安卓、火狐瀏覽器、火狐作業系統以及其它許多產品都使用它作為圖形引擎。

Skia 由谷歌出資管理,任何人都可基於 BSD 免費軟體許可證使用 Skia。Skia 開發團隊致力於開發其核心部分, 並廣泛採納各方對於 Skia 的開源貢獻。

因為沒有使用原生的 UI 和繪製框架,所以才保證了 Flutter 的高效能體驗。

Flutter Engine: 這是一個純 C++實現的 SDK,其中囊括了 Skia引擎、Dart執行時、文字排版引擎等。不過說白了,它就是 Dart的一個執行時,它可以以 JIT、JIT Snapshot 或者 AOT的模式執行 Dart程式碼。在程式碼呼叫 dart:ui庫時,提供 dart:ui庫中 Native Binding 實現。 不過別忘了,這個執行時還控制著 VSync訊號的傳遞、GPU資料的填充等,並且還負責把客戶端的事件傳遞到執行時中的程式碼。

sdk原始碼

…/sdk/flutter/packages/flutter/lib

Flutter原理深度解析

Flutter for Web

Flutter原理深度解析
通過對比,可以發現,web框架層和mobile的幾乎一模一樣。因此只需要重新實現一下引擎和嵌入層,不用變動Flutter API就可以完全可以將UI程式碼從Android / IOS Flutter App移植到Web。

Dart能夠使用Dart2Js編譯器把Dart程式碼編譯成Js程式碼。大多數原生App元素能夠通過DOM實現,DOM實現不了的元素可以通過Canvas來實現。

通訊

Flutter原理深度解析

得益於 Engine 層,Flutter 甚至不使用移動平臺的原生控制元件, 而是使用自己 Engine 來繪製 Widget (Flutter的顯示單元),而 Dart 程式碼都是通過 AOT 編譯為平臺的原生程式碼,所以 Flutter 可以 直接與平臺通訊,不需要JS引擎的橋接。同時 Flutter 唯一要求系統提供的是 canvas,以實現UI的繪製。

對於Android平臺,Flutter引擎的C/C++程式碼是由NDK編譯,在iOS平臺,則是由LLVM編譯,兩個平臺的Dart程式碼都是AOT編譯為原生程式碼,Flutter應用程式使用本機指令集執行。Flutter正是是通過使用相同的渲染器、框架和一組widget,來同時構建iOS和Android應用,而無需維護兩套獨立的程式碼庫。

通過platform channels 和本地進行通訊。

通過MethodChannel,Native也能呼叫Flutter的方法,這是一個雙向的通道。

Flutter原理深度解析

關於VSync

Android顯示器執行在60幀/秒左右,一幀大概16.6ms。

繪製間隔時間大於16ms會出現卡頓現象。

Flutter原理深度解析

Flutter原理深度解析

圖形渲染

Rendering Pipeline

在Flutter框架中存在著一個渲染流水線(Rendering pipline)。這個渲染流水線是由垂直同步訊號(Vsync)驅動的,而Vsync訊號是由系統提供的,如果你的Flutter app是執行在Android上的話,那Vsync訊號就是我們熟悉的Android的那個Vsync訊號。

當Vsync訊號到來以後,Flutter 框架會按照圖裡的順序執行一系列動作: 動畫(Animate)、構建(Build)、佈局(Layout)和繪製(Paint),最終生成一個場景(Scene)之後送往底層,由GPU繪製到螢幕上。

動畫(Animate)階段:因為動畫會隨每個Vsync訊號的到來而改變狀態(State),所以動畫階段是流水線的第一個階段。

構建(Build)在這個階段Flutter,在這個階段那些需要被重新構建的Widget會在此時被重新構建。也就是我們熟悉的StatelessWidget.build()或者State.build()被呼叫的時候。

佈局(Layout)階段,這時會確定各個顯示元素的位置,尺寸。此時是RenderObject.performLayout()被呼叫的時候。

繪製(Paint)階段,此時是RenderObject.paint()被呼叫的時候。

以上是整個渲染流水線的一個大致的工作過程。

Flutter app只有在狀態發生變化的時候需要觸發渲染流水線。當你的app什麼都不做的時候是不需要重新渲染頁面的。所以,Vsync訊號需要Flutter app去排程。比如我們都知道如果你的某個頁面需要發生變化的時候有可能會呼叫State.setState(),這個呼叫Flutter框架最終會發起一個排程Vsync訊號的請求給底層。然後底層會在Vsync訊號到來的時候驅動渲染流水線開始運作,最後把新的頁面顯示到螢幕上。

Flutter原理深度解析

Graphics Pipeline

Flutter框架渲染機制的一個示意圖。

整個渲染流水線是執行在UI執行緒裡的,以Vsync訊號為驅動,在框架渲染完成之後會輸出layer tree。

layer tree被送入engine,engine會把layer tree排程到GPU執行緒,在GPU執行緒內合成(compsite)layer tree,然後由Skia 2D渲染引擎渲染後送入GPU顯示。

這裡提到layer tree是因為我們即將要分析的渲染流水線繪製階段最終輸出就是這樣的layer tree。

所以繪製階段並不是簡單的呼叫paint()函式這麼簡單了,而是很多地方都涉及到layer tree的管理。

Flutter只關心向 GPU提供檢視資料,GPU的 VSync訊號同步到 UI執行緒,UI執行緒使用 Dart來構建抽象的檢視結構,這份資料結構在 GPU執行緒進行圖層合成,檢視資料提供給 Skia引擎渲染為 GPU資料,這些資料通過 OpenGL或者 Vulkan提供給 GPU。

Flutter原理深度解析

Widget

在Flutter中,大多數東西都是widget,而Widget是不可變的,僅支援一幀,並且在每一幀上不會直接更新,要更新而必須使用Widget的狀態。無狀態和有狀態 widget 的核心特性是相同的,每一幀它們都會重新構建,有一個State物件,它可以跨幀儲存狀態資料並恢復它。 Flutter 上 Android 自帶了 Skia,Skia是一個 2D的繪圖引擎庫,跨平臺,所以可以被嵌入到 Flutter的 iOS SDK中,也使得 Flutter Android SDK要比 iOS SDK小很多。

Flutter原理深度解析

Widget lifecycle

一個 StatelessWidget 是不能被改變的,比如:Icon、Text等。

如果你的控制元件一旦顯示,就不需要再做任何的變更,那麼你應該使用 StatelessWidget。

Flutter原理深度解析

一個 StatefulWidget 是有狀態的,可變的。

它可以改變自己的外觀,以響應使用者的操作或者資料的變化。

比如:CheckBox、Switch..

Flutter原理深度解析

State lifecycle

Flutter原理深度解析

相關文章