Flutter 高性能原理淺析
[圖片上傳失敗...(image-1a480b-1642738386010)]
北斗星_And關(guān)注
1<time datetime="2019-07-29T01:34:03.000Z" style="box-sizing: border-box; margin-right: 10px;">2019.07.29 09:34:03</time>字數(shù) 2,470閱讀 4,273
<article class="_2rhmJa" style="box-sizing: border-box; display: block; font-weight: 400; line-height: 1.8; margin-bottom: 20px; word-break: break-word; position: relative;">
這是我第三篇Flutter相關(guān)博客
歡迎 查看我的前兩篇 Flutter實現(xiàn)篇
前言
Flutter是Google用以幫助開發(fā)者在Ios和Android兩個平臺開發(fā)高質(zhì)量原生應用的全新移動UI框架.我開始認識Flutter時,經(jīng)歷了三個Flutter重要歷史版本.
- 2018年2月27日捺典,在2018世界移動大會上鸟廓,Google發(fā)布了Flutter的第一個Beta版本。
- 2018年6月21日襟己,在全球大前端技術(shù)大會上肝箱,發(fā)布了第一個Release Perview 1版本。
- 2018年12月5日第一個Release 1.0版本發(fā)布.
此后越來越多的人開始關(guān)注到Flutter稀蟋。
在 Flutter 誕生之前煌张,已經(jīng)有許多跨平臺 UI 框架的方案,比如基于 WebView 的 Cordova退客、AppCan 等骏融,還有使用 HTML+JavaScript 渲染成原生控件的 React Native、Weex 等萌狂。Flutter 則開辟了一種全新的思路档玻,從頭到 尾重寫一套跨平臺的 UI 框架,包括 UI 控件茫藏、渲染邏輯甚至開發(fā)語言误趴。
Flutter框架
從圖中可以看出 Flutter主要被分為兩層 Framework層和Flutter Engine.
Framework層全部使用Dart編寫,有完整UI框架的API务傲,并預寫了Android(MaterialDesign)和IOS的(Cupertino)風格的UI,極大方便了開發(fā)移動端.
Framework 底層是 Flutter 引擎凉当, 引擎主要負責圖形繪制 (Skia)、 文字排版 (libtxt) 和提供 Dart 運行時售葡, 引擎全部使用 C++實現(xiàn).
Flutter高性能原理
與其他跨平臺框架對比
在看Flutter框架前看杭,我們先看一下其他跨平臺框架的設計
看Hybrid的架構(gòu),我們可以知道UI層的渲染是基于Webview去渲染挟伙,他的性能取決于webview的渲染性能楼雹,目前已知webview渲染性能 < NativeUI的性能
RN/Weex 的架構(gòu)中,是基于Native的UI框架去適配,中間多了一層js轉(zhuǎn)NativeUI的過程
而Flutter不需要中間層(Webview贮缅,js 轉(zhuǎn)NativeUI這個過程)榨咐,他是基于圖像渲染引擎去直接繪制UI.
Dart 對于UI框架的高性能支持
我們知道Flutter的Framework層是使用了Dart語言編寫,那Dart語言有哪些優(yōu)勢呢谴供?下面分為幾個點來闡述
Dart內(nèi)存分配機制
DartVM的內(nèi)存分配策略非常簡單块茁,創(chuàng)建對象時只需要在現(xiàn)有堆上移動指針,內(nèi)存增長始終是線形的,省去了查找可用內(nèi)存段的過程
Dart中類似線程的概念叫做Isolate憔鬼,每個Isolate之間是無法共享內(nèi)存的龟劲,所以這種分配策略可以讓Dart實現(xiàn)無鎖的快速分配胃夏。
Dart 垃圾回收機制
Dart的垃圾回收也采用了多生代算法轴或,新生代在回收內(nèi)存時采用了“半空間”算法,觸發(fā)垃圾回收時Dart會將當前半空間中的“活躍”對象拷貝到備用空間仰禀,然后整體釋放當前空間的所有內(nèi)存如圖.
整個過程中Dart只需要操作少量的“活躍”對象照雁,大量的沒有引用的“死亡”對象則被忽略,這種 多生代無鎖垃圾回收器答恶,專門為UI框架中常見的大量Widgets對象創(chuàng)建和銷毀優(yōu)化,非常適合Flutter框架中大量Widget重建的場景.
Dart 編體積優(yōu)化饺蚊,及編譯JIT和AOT支持
代碼體積優(yōu)化(Tree Shaking),編譯時只保留運行時需要調(diào)用的代碼(不允許反射這樣的隱式引用)悬嗓,所以龐大的Widgets庫不會造成發(fā)布體積過大污呼。
Dart支持兩種編譯模式:
- JIT編譯 Just In Time Compiler -即時編譯
- AOT編譯Ahead Of Time 預編譯
在debug模式下使用JIT編譯,生成srcipt/bytecode進行解釋執(zhí)行包竹,可以支持HotReload(熱重載)燕酷,修改代碼,保持即可在設備上看到效果. 而在Release下 AOT編譯生成Machine Code周瞎,高效的運行.
Dart 單線程 異步消息機制
客戶端交互簡述
對于移動端的交互來說苗缩,大多數(shù)情況下都是在等待狀態(tài),等待網(wǎng)絡請求声诸,等待用戶輸入等.那么設想一下酱讶,發(fā)起一個網(wǎng)絡請求只在一個線程中可以進行嗎?當然網(wǎng)絡請求肯定是異步的(注意這里說的異步而多線程并非一個概念.)彼乌,事實驗證是可以的泻肯,F(xiàn)lutter就采用了Dart這種單線程機制,省去了多線程上下文切換帶來的性能損耗.(對于高耗時操作慰照,也同樣支持多線程操作软免,通過Isolate開啟,不過注意這里的多線程焚挠,內(nèi)存是無法共享的.)
Dart 異步消息原理
當一個Dart的方法開始執(zhí)行時膏萧,他會一直執(zhí)行直至達到這個方法的退出點。換句話說Dart的方法是不會被其他Dart代碼打斷的。
當一個Dart應用開始的標志是它的main isolate執(zhí)行了main方法榛泛。當main方法退出后蝌蹂,main isolate的線程就會去逐一處理消息隊列中的消息。
有了消息隊列曹锨,然后有了循環(huán)去讀取消息隊列中的消息孤个,就可以有單線程去執(zhí)行異步消息的能力.
一般的消息使用dart:async中使用Future來支持異步消息.
Flutter Engine 高性能
在講Flutter Engin層時,我們先講一下屏幕繪制的原理.
屏幕繪制原理
我們都知道顯示器以固定的頻率刷新沛简,比如 iPhone的 60Hz齐鲤、iPad Pro的 120Hz。當一幀圖像繪制完畢后準備繪制下一幀時椒楣,顯示器會發(fā)出一個垂直同步信號(VSync)给郊,所以 60Hz的屏幕就會一秒內(nèi)發(fā)出 60次這樣的信號。
并且一般地來說捧灰,計算機系統(tǒng)中淆九,CPU、GPU和顯示器以一種特定的方式協(xié)作:CPU將計算好的顯示內(nèi)容提交給 GPU毛俏,GPU渲染后放入幀緩沖區(qū)炭庙,然后視頻控制器按照 VSync信號從幀緩沖區(qū)取幀數(shù)據(jù)傳遞給顯示器顯示。
作為一個專職Android開發(fā)煌寇,看過Android的繪圖機制焕蹄,通過SurfaceFlinger 和HAL層之間的工作機制發(fā)現(xiàn)和Flutter的很像,那么IOS的如何呢阀溶?個人推測屏幕的繪圖機制是一樣的腻脏,只是不同平臺有不同實現(xiàn).
Flutter Engine的渲染機制
Flutter只關(guān)心向 GPU提供視圖數(shù)據(jù),GPU的 VSync信號同步到 UI線程淌哟,UI線程使用 Dart來構(gòu)建抽象的視圖結(jié)構(gòu)迹卢,這份數(shù)據(jù)結(jié)構(gòu)在 GPU線程進行圖層合成,視圖數(shù)據(jù)提供給 Skia引擎渲染為 GPU數(shù)據(jù)徒仓,這些數(shù)據(jù)通過 OpenGL或者 Vulkan提供給 GPU.
所以 Flutter并不關(guān)心顯示器腐碱、視頻控制器以及 GPU具體工作,它只關(guān)心 GPU發(fā)出的 VSync信號掉弛,盡可能快地在兩個 VSync信號之間計算并合成視圖數(shù)據(jù)症见,并且把數(shù)據(jù)提供給 GPU.
Flutter Framework層的繪圖機制
UI樹原理
在 Flutter 界面渲染過程分為 3 個階段: 布局、繪制殃饿、合成.
而布局階段谋作,有三個重要的對象.RenderObject、Element乎芳、Widget.
Widget是開發(fā)經(jīng)常接觸的控件,默認是只讀的.
Element 是 Flutter 用來分離控件樹和真正的渲染 對象的中間層遵蚜, 控件用來描述對應的 element 屬性帖池,控件重建后可能會復用同一個 element.
RenderObject 負責提供配置信息并創(chuàng)建具體的 Element。
Element 持有真正負責布局吭净、 繪制和碰撞測試 (hit test) 的 RenderObject 對象.
那么這樣睡汹,如果控件的屬性發(fā)生了變化 (因為控件的屬性是只 讀的, 所以變化也就意味著重新創(chuàng)建了新的控件樹)寂殉, 但是其樹上每個節(jié)點的類型沒有變化時囚巴, element 樹和 render 樹可以完全重用原來的對象 (因為 element 和 render object 的屬性都是可變的)
布局原理
傳統(tǒng)布局,如Android可能需要多次Measure,計算寬高友扰。Flutter 采用約束進行單次測量布局. 整個布局過程中只需要深度遍歷一次彤叉,極大的提升效能。
渲染對象樹中的每個對象都會在布局過程中接受父 對象的 Constraints 參數(shù),決定自己的大小, 然后父對象 就可以按照自己的邏輯決定各個子對象的位置,完成布局過程.
子對象不存儲自己在容器中的位置村怪, 所以在它的位置發(fā)生改變時并不需要重新布局或者繪制. 子對象的位 置信息存儲在它自己的 parentData 字段中,但是該字段由它的父對象負責維護,自身并不關(guān)心該字段的內(nèi)容秽浇。
同時也因為這種簡單的布局邏輯, Flutter 可以在某些節(jié) 點設置布局邊界 (Relayout boundary)实愚, 即當邊界內(nèi)的任 何對象發(fā)生重新布局時兼呵, 不會影響邊界外的對象兔辅, 反之亦然.
</article>