以前,寫了一篇《iOS 淺談GPU及「App渲染流程」》闡述了iOS端App的渲染流程。
其中包括三種渲染方式,分別是:
1.iOS原生渲染:使用原生控件、原生語言編寫。
2.大前端渲染(一種是WebView
,另外一種是JSCore
引擎做爲虛擬機)。
3.Flutter渲染。前端
《上篇博客》主要講解了iOS APP
渲染的流程,以及GPU的渲染流水線。
但關於Flutter是如何渲染的?我上篇寫的並非很透徹。
因此本篇將和你們一塊兒談談 —— 「 Flutter 到底是如何渲染的? 」api
先從Flutter的總體架構提及,共分爲三層,又下到上分別爲:Embedder
層、Engine
層、Framework
層。架構
分別對應:佈局
那麼,這三層的職責是什麼?究竟分別作了哪些事呢?性能
從上到下開始,先從咱們相對比較熟悉的 Framework
層提及。測試
實現了:Animation
(動畫)、Painting
(圖形繪製)、Gestures
(手勢操做)等功能,幷包裝成對應的 api
提供給上層開發者調用。
爲了保證Flutter所繪製的控件與原生控件風格相似,Flutter封裝了Material
(對應Android
)、Cupertino
(對應iOS
)風格的UI組件庫,供開發者直接使用。優化
這層主要包含三塊:Skia
、Dart
、Text
。
Skia
是渲染引擎,爲 Framework
層提供「底層渲染」能力。
Dart
是 Dart
運行時引擎,爲 Framework
層提供運行時調用Dart和渲染能力。
Text
是文字排版,爲 Framework
層提供視圖排版能力。動畫
對不一樣平臺操做系統的適配,包括一些配置:surface、線程、插件等特性。
因爲Flutter相關特性並很少,所以對不一樣平臺操做系統的適配成本很低。操作系統
咱們都知道,在Flutter中,Everything is widget
。.net
全部 Widget
會組成 Widget Tree
。
界面更新時,會更新 Widget Tree
,
再更新 Element Tree
,最後更新 RenderObjectTree
。
那麼,究竟Flutter是怎麼更新界面的?又有哪些優化的渲染策略呢??
分爲4個階段,分別是:
佈局階段 => 繪製階段 => 合成階段 => 渲染階段
(Layout
=> Paint
=> Composite
=> Rasterize
)
Flutter採用 「深度優先」 機制遍歷Widget Tree。
咱們都知道,在佈局過程當中,Widget樹中的每一個孩子節點都會受到父節點所加的位置約束。(也就是說,父節點決定孩子節點的位置,孩子節點只能決定本身的大小。)
先肯定父節點的大小、位置,
再肯定孩子節點的位置,
再肯定孩子節點的大小。
....一直往下,
直到沒有孩子Widget
後,返回完成。
PS:深度優先於廣度優先的區別:參考博客
爲了防止孩子節點的變化,致使整個 Widget Tree
從新佈局。
Flutter加入了 「佈局邊界」 機制(Relayout Boundary)。(劃重點,★優化點★)
在某些節點,「自動」或「手動」加上 「佈局邊界」 ,控制邊界。
在該佈局邊界內的任何節點發生從新佈局,都不會影響邊界外的 Widget Tree
的佈局。
佈局完成後,樹上每一個節點都肯定了「尺寸大小」和「位置」。
剛纔,咱們肯定了樹上的控件的「尺寸」與「位置」。
接下來是繪製階段。
和佈局相似,Flutter也是採用 「深度優先」 機制遍歷渲染樹。
先繪製自身,再繪製子節點。
可是,會出現一些繪製圖層覆蓋問題。
好比,當節點2須要重繪時,節點二、五、6一塊兒重繪了(其實只要重繪節點二、5)。
爲了解決繪製覆蓋問題,Flutter採用了也是和佈局階段類似的策略: 重繪邊界 機制(Repaint Boundary)。
其實,本質上就是加個新的圖層,避免在同一圖層重繪產生影響。(劃重點,★優化點★)
典型的例子是,ScrollView。
一旦設置好重繪邊界,滾動時,只會重繪ScollView中的視圖內容,而其餘部分不用從新繪製。(劃重點,★優化點★)
因爲繪製出來的渲染樹,會有不少層,同步多層渲染會出現性能問題。
所以,Flutter會在渲染前,將多個渲染樹圖層進行合成。(劃重點,★優化點★)
根據多層渲染樹的大小、層級、透明度等計算後,
合成爲最終「簡化版」的渲染樹,以提升下一步的渲染效率。
將處理過的「簡化版」渲染樹,交給Skia
引擎轉換成「二維圖像數據」。
而後 Skia
把計算好的圖形數據,經過 OpenGL
接口交給 GPU
渲染,走上一篇《iOS 淺談GPU及「App渲染流程」》中提到的 GPU
工做流水線:
頂點着色器 => 形狀裝配 => 幾何着色器 => 光柵化 => 片斷着色器 => 測試與混合。
而後,GPU工做流水線六階段完成。
最終,展現到咱們的終端屏幕上。
固然這只是一個垂直同步信號(VSync)的過程。(按60fps算,一秒須要60個VSync纔不會感到卡頓。)
按60fps算,一秒須要60個VSync纔不會感到卡頓。
在流水線的Widget更新,Flutter也有優化的點:
簡單來講,就是chind節點能複用就複用。不能複用,就從新繪製。
更新Widget
的邏輯以下:
\ | newWidget == null | newWidget != null |
---|---|---|
child == null | 返回null | 返回新的Element |
child != null | 移除舊的child並返回null | 若是舊child被更新就返回child,不然返回新的Element |
參考與致謝:
1.《Flutter核心技術與實戰》(陳航老師)
2. 小德 -- Flutter 技術分享
3.《Flutter從加載到顯示》 (聖文前輩)