Flutter原理簡介


Flutter 是怎么運轉的?

與用於構建移動應用程序的其他大多數框架不同,Flutter 是重寫了一整套包括底層渲染邏輯和上層開發語言的完整解決方案。這樣不僅可以保證視圖渲染在 Android 和 iOS 上的高度一致性(即高保真),在代碼執行效率和渲染性能上也可以媲美原生 App 的體驗(即高性能)。

這,就是 Flutter 和其他跨平台方案的本質區別:

  • React Native 之類的框架,只是通過 JavaScript 虛擬機擴展調用系統組件,由 Android 和 iOS 系統進行組件的渲染;
  • Flutter 則是自己完成了組件渲染的閉環。

那么,Flutter 是怎么完成組件渲染的呢?這需要從圖像顯示的基本原理說起。

在計算機系統中,圖像的顯示需要 CPU、GPU 和顯示器一起配合完成:CPU 負責圖像數據計算,GPU 負責圖像數據渲染,而顯示器則負責最終圖像顯示。

CPU 把計算好的、需要顯示的內容交給 GPU,由 GPU 完成渲染后放入幀緩沖區,隨后視頻控制器根據垂直同步信號(VSync)以每秒 60 次的速度,從幀緩沖區讀取幀數據交由顯示器完成圖像顯示。

操作系統在呈現圖像時遵循了這種機制,而 Flutter 作為跨平台開發框架也采用了這種底層方案。下面有一張更為詳盡的示意圖來解釋 Flutter 的繪制原理。

img
圖 1 Flutter 繪制原理

可以看到,Flutter 關注如何盡可能快地在兩個硬件時鍾的 VSync 信號之間計算並合成視圖數據,然后通過 Skia 交給 GPU 渲染:UI 線程使用 Dart 來構建視圖結構數據,這些數據會在 GPU 線程進行圖層合成,隨后交給 Skia 引擎加工成 GPU 數據,而這些數據會通過 OpenGL 最終提供給 GPU 渲染。

在進一步學習 Flutter 之前,我們有必要了解下構建 Flutter 的關鍵技術,即 Skia 和 Dart。

Skia 是什么?

要想了解 Flutter,你必須先了解它的底層圖像渲染引擎 Skia。因為,Flutter 只關心如何向 GPU 提供視圖數據,而 Skia 就是它向 GPU 提供視圖數據的好幫手。

Skia 是一款用 C++ 開發的、性能彪悍的 2D 圖像繪制引擎,其前身是一個向量繪圖軟件。2005 年被 Google 公司收購后,因為其出色的繪制表現被廣泛應用在 Chrome 和 Android 等核心產品上。Skia 在圖形轉換、文字渲染、位圖渲染方面都表現卓越,並提供了開發者友好的 API。

目前,Skia 已然是 Android 官方的圖像渲染引擎了,因此 Flutter Android SDK 無需內嵌 Skia 引擎就可以獲得天然的 Skia 支持;而對於 iOS 平台來說,由於 Skia 是跨平台的,因此它作為 Flutter iOS 渲染引擎被嵌入到 Flutter 的 iOS SDK 中,替代了 iOS 閉源的 Core Graphics/Core Animation/Core Text,這也正是 Flutter iOS SDK 打包的 App 包體積比 Android 要大一些的原因。

底層渲染能力統一了,上層開發接口和功能體驗也就隨即統一了,開發者再也不用操心平台相關的渲染特性了。也就是說,Skia 保證了同一套代碼調用在 Android 和 iOS 平台上的渲染效果是完全一致的。

為什么是 Dart?

Dart 因為同時支持 AOT 和 JIT,所以具有運行速度快、執行性能好的特點外,Flutter 為什么選擇了 Dart,而不是前端應用的准官方語言 JavaScript 呢?這個問題很有意思,但也很有爭議。

Google 公司給出的原因很簡單也很直接:Dart 語言開發組就在隔壁,對於 Flutter 需要的一些語言新特性,能夠快速在語法層面落地實現;而如果選擇了 JavaScript,就必須經過各種委員會和瀏覽器提供商漫長的決議。

Flutter 的原理

在了解了 Flutter 的基本運作機制后,我們再來深入了解一下 Flutter 的實現原理。

首先,我們來看一下 Flutter 的架構圖。我希望通過這張圖以及對應的解讀,你能在開始學習的時候就建立起對 Flutter 的整體印象,能夠從框架設計和實現原理的高度去理解 Flutter 區別其他跨平台解決方案的關鍵所在,為后面的學習打好基礎,而不是直接一上來就陷入語言和框架的功能細節“泥潭”而無法自拔。

img
圖 2 Flutter 架構圖

備注:此圖引自Flutter System Overview

Flutter 架構采用分層設計,從下到上分為三層,依次為:Embedder、Engine、Framework。

  • Embedder 是操作系統適配層,實現了渲染 Surface 設置,線程設置,以及平台插件等平台相關特性的適配。從這里我們可以看到,Flutter 平台相關特性並不多,這就使得從框架層面保持跨端一致性的成本相對較低。
  • Engine 層主要包含 Skia、Dart 和 Text,實現了 Flutter 的渲染引擎、文字排版、事件處理和 Dart 運行時等功能。Skia 和 Text 為上層接口提供了調用底層渲染和排版的能力,Dart 則為 Flutter 提供了運行時調用 Dart 和渲染引擎的能力。而 Engine 層的作用,則是將它們組合起來,從它們生成的數據中實現視圖渲染。
  • Framework 層則是一個用 Dart 實現的 UI SDK,包含了動畫、圖形繪制和手勢識別等功能。為了在繪制控件等固定樣式的圖形時提供更直觀、更方便的接口,Flutter 還基於這些基礎能力,根據 Material 和 Cupertino 兩種視覺設計風格封裝了一套 UI 組件庫。我們在開發 Flutter 的時候,可以直接使用這些組件庫。

接下來,我以界面渲染過程為例,和你介紹 Flutter 是如何工作的。

頁面中的各界面元素(Widget)以樹的形式組織,即控件樹。Flutter 通過控件樹中的每個控件創建不同類型的渲染對象,組成渲染對象樹。而渲染對象樹在 Flutter 的展示過程分為四個階段:布局、繪制、合成和渲染。

布局

Flutter 采用深度優先機制遍歷渲染對象樹,決定渲染對象樹中各渲染對象在屏幕上的位置和尺寸。在布局過程中,渲染對象樹中的每個渲染對象都會接收父對象的布局約束參數,決定自己的大小,然后父對象按照控件邏輯決定各個子對象的位置,完成布局過程。

img 圖 3 Flutter 布局過程

為了防止因子節點發生變化而導致整個控件樹重新布局,Flutter 加入了一個機制——布局邊界(Relayout Boundary),可以在某些節點自動或手動地設置布局邊界,當邊界內的任何對象發生重新布局時,不會影響邊界外的對象,反之亦然。

img 圖 4 Flutter 布局邊界

繪制

布局完成后,渲染對象樹中的每個節點都有了明確的尺寸和位置。Flutter 會把所有的渲染對象繪制到不同的圖層上。與布局過程一樣,繪制過程也是深度優先遍歷,而且總是先繪制自身,再繪制子節點。

以下圖為例:節點 1 在繪制完自身后,會再繪制節點 2,然后繪制它的子節點 3、4 和 5,最后繪制節點 6。

img 圖 5 Flutter 繪制示例

可以看到,由於一些其他原因(比如,視圖手動合並)導致 2 的子節點 5 與它的兄弟節點 6 處於了同一層,這樣會導致當節點 2 需要重繪的時候,與其無關的節點 6 也會被重繪,帶來性能損耗。

為了解決這一問題,Flutter 提出了與布局邊界對應的機制——重繪邊界(Repaint Boundary)。在重繪邊界內,Flutter 會強制切換新的圖層,這樣就可以避免邊界內外的互相影響,避免無關內容置於同一圖層引起不必要的重繪。

img 圖 6 Flutter 重繪邊界

重繪邊界的一個典型場景是 Scrollview。ScrollView 滾動的時候需要刷新視圖內容,從而觸發內容重繪。而當滾動內容重繪時,一般情況下其他內容是不需要重繪的,這時候重繪邊界就派上用場了。

合成和渲染

終端設備的頁面越來越復雜,因此 Flutter 的渲染樹層級通常很多,直接交付給渲染引擎進行多圖層渲染,可能會出現大量渲染內容的重復繪制,所以還需要先進行一次圖層合成,即將所有的圖層根據大小、層級、透明度等規則計算出最終的顯示效果,將相同的圖層歸類合並,簡化渲染樹,提高渲染效率。

合並完成后,Flutter 會將幾何圖層數據交由 Skia 引擎加工成二維圖像數據,最終交由 GPU 進行渲染,完成界面的展示。這部分內容,我已經在前面的內容中介紹過,這里就不再贅述了。

接下來,我們再看看學習 Flutter,都需要學習哪些知識。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM