Flutter_02_原理概述

是桃子呀發表於2020-01-30

Flutter、Golang、Python、編譯原理、演算法、Chrome原理學習系列文章搶先看請關注【碼農幫派】:

Flutter重寫了一套包括底層渲染邏輯 + 上層開發語言的完整方案,保證了:

  • 高保真:保證檢視在Android和IOS平臺上高度一致

  • 高效能:程式碼執行的效率媲美原生App的體驗

Flutter與其他跨平臺方案的區別:

  • RN之類的框架,通過JavaScript虛擬機器呼叫系統原生元件,由Android和IOS系統進行元件的渲染;

  • Flutter是自己完成元件的渲染

在計算機系統中,影像的顯示需要CPU、GPU和顯示器一起配合完成:

  • CPU負責影像資料的計算

  • GPU負責影像資料的渲染

  • 顯示器負責最終影像的顯示

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

作業系統在呈現影像時遵循了這種機制,而Flutter作為跨平臺開發框架也採用這種底層方案:

Flutter_02_原理概述

上圖中,Flutter 關注如何儘可能快地在兩個硬體時鐘的 VSync 訊號之間計算併合成檢視資料,然後通過 Skia 交給 GPU 渲染:UI 執行緒使用 Dart 來構建檢視結構資料,這些資料會在 GPU 執行緒進行圖層合成,隨後交給 Skia 引擎加工成 GPU 資料,而這些資料會通過 OpenGL 最終提供給 GPU 渲染。

Skia是一款2D影像繪製引擎,是Android官方影像繪製引擎,因此在Android Flutter中無需嵌入Skia引擎就可以天然得到Skia的支援,而IOS系統中並沒有內建Skia引擎,所以Flutter IOS打包的App包體積比Android要大一些。


Dart語言作為Flutter的開發語言:

1. Dart同時支援了JIT和AOT兩種編譯模式,JIT(Just In Time)即時編譯,便於在開發期除錯;AOT(Ahead Of Time)事前編譯,提高程式執行效率。

2. Dart避免了搶佔式排程和共享記憶體,可以在沒有鎖的情況下進行物件的分配和垃圾回收,在效能上表現相當不錯。

Flutter原理:

Flutter_02_原理概述

Flutter架構採用分層設計,從下到上分為3層:Embedder、Engine、Framework。

Embeder層是作業系統適配層,實現了渲染Surface設定、執行緒設定,以及平臺外掛等平臺相關特性的適配。

Engine層主要包括Skia、Dart和Text,實現了Flutter的渲染引擎、文字排版、事件處理和Dart執行的能力。Skia和Text為上層介面提供了呼叫底層渲染和排版的能力,Dart則為Flutter提供了執行時呼叫Dart和渲染引擎的能力。Engine層則是將它們組合起來,從它們生成的資料中實現檢視渲染。

Framework層是一個用Dart實現的UI SDK,包含了動畫、圖形繪製和手勢識別等功能,在Framework層Flutter提供了Material和Cupertino兩種風格的元件庫。


Flutter工作原理/流程:

Flutter介面中各個元素(Widget)以樹的形式組織,即控制元件樹。Flutter通過控制元件樹上的每個控制元件建立不同型別的渲染物件,組成了渲染物件樹。渲染物件樹在Flutter的展示過程分為4個階段:

  • 佈局

  • 繪製

  • 合成

  • 渲染

佈局:

Flutter採用深度優先策略遍歷整個渲染物件樹,決定渲染物件在螢幕中的位置和尺寸。在佈局過程中每個渲染物件都會接收父物件的佈局約束引數,決定自己的大小,父物件按照控制元件佈局邏輯決定每個子物件的位置,完成佈局過程。

佈局邊界:為了防止因子節點發生變化而導致整個控制元件樹重新佈局,Flutter引入了機制:佈局邊界(Relayout Boundary),可以在某些節點上自動或手動設定佈局邊界,當佈局邊界內的任何物件發生重新佈局的時候,都不會影響邊界外的物件,反之邊界外的物件發生重新佈局,也不會影響邊界內的物件。

繪製:

佈局完成之後,渲染物件樹的每個節點都有了明確的尺寸和位置,Flutter會把所有的渲染物件繪製到不同的圖層上。繪製的過程也是採用深度優先策略,而且總是先繪製自身,再繪製子節點。

Flutter_02_原理概述

上圖中,節點1在繪製完自身後,會再繪製節點2,然後繪製它的子節點3、4 和 5,最後繪製節點6。由於某些原因(e.g. 檢視手動合併)導致節點2的子節點5和它的兄弟節點6處於同一層,這樣會導致當節點2需要重繪的時候,與其無關的節點6也會被重繪,帶來效能的損耗。

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

Flutter_02_原理概述

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

合成和渲染:

當繪製介面複雜的時候,Flutter的渲染樹層級通常會很多,直接交付給渲染引擎進行多圖層的渲染,可能會出現大量渲染內容重複繪製,所以還需要進行一次圖層合併,即將所有的圖層按照大小、層級、透明度等規則計算出最終的顯示效果,將相同的圖層進行歸類合併,簡化渲染樹,提高渲染引擎的渲染效率。

合併完成之後,Flutter會將幾何圖層資料交由Skia引擎加工成二維影像資料,最終由GPU進行渲染,完成介面的展示。




相關文章