這是我第三篇Flutter相關(guān)博客
歡迎 查看我的前兩篇 Flutter實(shí)現(xiàn)篇
前言
Flutter是Google用以幫助開發(fā)者在Ios和Android兩個(gè)平臺開發(fā)高質(zhì)量原生應(yīng)用的全新移動UI框架.我開始認(rèn)識Flutter時(shí)几莽,經(jīng)歷了三個(gè)Flutter重要?dú)v史版本.
- 2018年2月27日招盲,在2018世界移動大會上屡久,Google發(fā)布了Flutter的第一個(gè)Beta版本拷呆。
- 2018年6月21日拴泌,在全球大前端技術(shù)大會上杆怕,發(fā)布了第一個(gè)Release Perview 1版本捷绑。
- 2018年12月5日第一個(gè)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飞蹂,并預(yù)寫了Android(MaterialDesign)和IOS的(Cupertino)風(fēng)格的UI,極大方便了開發(fā)移動端.
Framework 底層是 Flutter 引擎, 引擎主要負(fù)責(zé)圖形繪制 (Skia)翻屈、 文字排版 (libtxt) 和提供 Dart 運(yùn)行時(shí)陈哑, 引擎全部使用 C++實(shí)現(xiàn).
Flutter高性能原理
與其他跨平臺框架對比
在看Flutter框架前,我們先看一下其他跨平臺框架的設(shè)計(jì)
看Hybrid的架構(gòu)伸眶,我們可以知道UI層的渲染是基于Webview去渲染惊窖,他的性能取決于webview的渲染性能,目前已知webview渲染性能 < NativeUI的性能
RN/Weex 的架構(gòu)中厘贼,是基于Native的UI框架去適配界酒,中間多了一層js轉(zhuǎn)NativeUI的過程
而Flutter不需要中間層(Webview,js 轉(zhuǎn)NativeUI這個(gè)過程)嘴秸,他是基于圖像渲染引擎去直接繪制UI.
Dart 對于UI框架的高性能支持
我們知道Flutter的Framework層是使用了Dart語言編寫毁欣,那Dart語言有哪些優(yōu)勢呢?下面分為幾個(gè)點(diǎn)來闡述
Dart內(nèi)存分配機(jī)制
DartVM的內(nèi)存分配策略非常簡單岳掐,創(chuàng)建對象時(shí)只需要在現(xiàn)有堆上移動指針凭疮,內(nèi)存增長始終是線形的,省去了查找可用內(nèi)存段的過程
Dart中類似線程的概念叫做Isolate,每個(gè)Isolate之間是無法共享內(nèi)存的串述,所以這種分配策略可以讓Dart實(shí)現(xiàn)無鎖的快速分配执解。
Dart 垃圾回收機(jī)制
Dart的垃圾回收也采用了多生代算法,新生代在回收內(nèi)存時(shí)采用了“半空間”算法纲酗,觸發(fā)垃圾回收時(shí)Dart會將當(dāng)前半空間中的“活躍”對象拷貝到備用空間衰腌,然后整體釋放當(dāng)前空間的所有內(nèi)存如圖.
整個(gè)過程中Dart只需要操作少量的“活躍”對象,大量的沒有引用的“死亡”對象則被忽略觅赊,這種 多生代無鎖垃圾回收器右蕊,專門為UI框架中常見的大量Widgets對象創(chuàng)建和銷毀優(yōu)化,非常適合Flutter框架中大量Widget重建的場景.
Dart 編體積優(yōu)化,及編譯JIT和AOT支持
代碼體積優(yōu)化(Tree Shaking)吮螺,編譯時(shí)只保留運(yùn)行時(shí)需要調(diào)用的代碼(不允許反射這樣的隱式引用)饶囚,所以龐大的Widgets庫不會造成發(fā)布體積過大。
Dart支持兩種編譯模式:
- JIT編譯 Just In Time Compiler -即時(shí)編譯
- AOT編譯Ahead Of Time 預(yù)編譯
在debug模式下使用JIT編譯规脸,生成srcipt/bytecode進(jìn)行解釋執(zhí)行坯约,可以支持HotReload(熱重載)熊咽,修改代碼莫鸭,保持即可在設(shè)備上看到效果. 而在Release下 AOT編譯生成Machine Code,高效的運(yùn)行.
Dart 單線程 異步消息機(jī)制
客戶端交互簡述
對于移動端的交互來說横殴,大多數(shù)情況下都是在等待狀態(tài)被因,等待網(wǎng)絡(luò)請求卿拴,等待用戶輸入等.那么設(shè)想一下,發(fā)起一個(gè)網(wǎng)絡(luò)請求只在一個(gè)線程中可以進(jìn)行嗎梨与?當(dāng)然網(wǎng)絡(luò)請求肯定是異步的(注意這里說的異步而多線程并非一個(gè)概念.)堕花,事實(shí)驗(yàn)證是可以的,F(xiàn)lutter就采用了Dart這種單線程機(jī)制粥鞋,省去了多線程上下文切換帶來的性能損耗.(對于高耗時(shí)操作缘挽,也同樣支持多線程操作,通過Isolate開啟呻粹,不過注意這里的多線程壕曼,內(nèi)存是無法共享的.)
Dart 異步消息原理
當(dāng)一個(gè)Dart的方法開始執(zhí)行時(shí),他會一直執(zhí)行直至達(dá)到這個(gè)方法的退出點(diǎn)等浊。換句話說Dart的方法是不會被其他Dart代碼打斷的腮郊。
當(dāng)一個(gè)Dart應(yīng)用開始的標(biāo)志是它的main isolate執(zhí)行了main方法。當(dāng)main方法退出后筹燕,main isolate的線程就會去逐一處理消息隊(duì)列中的消息轧飞。
有了消息隊(duì)列,然后有了循環(huán)去讀取消息隊(duì)列中的消息撒踪,就可以有單線程去執(zhí)行異步消息的能力.
一般的消息使用dart:async中使用Future來支持異步消息.
Flutter Engine 高性能
在講Flutter Engin層時(shí)过咬,我們先講一下屏幕繪制的原理.
屏幕繪制原理
我們都知道顯示器以固定的頻率刷新,比如 iPhone的 60Hz制妄、iPad Pro的 120Hz援奢。當(dāng)一幀圖像繪制完畢后準(zhǔn)備繪制下一幀時(shí),顯示器會發(fā)出一個(gè)垂直同步信號(VSync)忍捡,所以 60Hz的屏幕就會一秒內(nèi)發(fā)出 60次這樣的信號集漾。
并且一般地來說,計(jì)算機(jī)系統(tǒng)中砸脊,CPU具篇、GPU和顯示器以一種特定的方式協(xié)作:CPU將計(jì)算好的顯示內(nèi)容提交給 GPU,GPU渲染后放入幀緩沖區(qū)凌埂,然后視頻控制器按照 VSync信號從幀緩沖區(qū)取幀數(shù)據(jù)傳遞給顯示器顯示驱显。
作為一個(gè)專職Android開發(fā),看過Android的繪圖機(jī)制瞳抓,通過SurfaceFlinger 和HAL層之間的工作機(jī)制發(fā)現(xiàn)和Flutter的很像埃疫,那么IOS的如何呢?個(gè)人推測屏幕的繪圖機(jī)制是一樣的孩哑,只是不同平臺有不同實(shí)現(xiàn).
Flutter Engine的渲染機(jī)制
Flutter只關(guān)心向 GPU提供視圖數(shù)據(jù)栓霜,GPU的 VSync信號同步到 UI線程,UI線程使用 Dart來構(gòu)建抽象的視圖結(jié)構(gòu)横蜒,這份數(shù)據(jù)結(jié)構(gòu)在 GPU線程進(jìn)行圖層合成胳蛮,視圖數(shù)據(jù)提供給 Skia引擎渲染為 GPU數(shù)據(jù)销凑,這些數(shù)據(jù)通過 OpenGL或者 Vulkan提供給 GPU.
所以 Flutter并不關(guān)心顯示器、視頻控制器以及 GPU具體工作仅炊,它只關(guān)心 GPU發(fā)出的 VSync信號斗幼,盡可能快地在兩個(gè) VSync信號之間計(jì)算并合成視圖數(shù)據(jù),并且把數(shù)據(jù)提供給 GPU.
Flutter Framework層的繪圖機(jī)制
UI樹原理
在 Flutter 界面渲染過程分為 3 個(gè)階段: 布局抚垄、繪制蜕窿、合成.
而布局階段,有三個(gè)重要的對象.RenderObject呆馁、Element渠羞、Widget.
Widget是開發(fā)經(jīng)常接觸的控件,默認(rèn)是只讀的.
Element 是 Flutter 用來分離控件樹和真正的渲染 對象的中間層, 控件用來描述對應(yīng)的 element 屬性智哀,控件重建后可能會復(fù)用同一個(gè) element.
RenderObject 負(fù)責(zé)提供配置信息并創(chuàng)建具體的 Element次询。
Element 持有真正負(fù)責(zé)布局、 繪制和碰撞測試 (hit test) 的 RenderObject 對象.
那么這樣瓷叫,如果控件的屬性發(fā)生了變化 (因?yàn)榭丶膶傩允侵?讀的屯吊, 所以變化也就意味著重新創(chuàng)建了新的控件樹), 但是其樹上每個(gè)節(jié)點(diǎn)的類型沒有變化時(shí)摹菠, element 樹和 render 樹可以完全重用原來的對象 (因?yàn)?element 和 render object 的屬性都是可變的)
布局原理
傳統(tǒng)布局盒卸,如Android可能需要多次Measure,計(jì)算寬高。Flutter 采用約束進(jìn)行單次測量布局. 整個(gè)布局過程中只需要深度遍歷一次次氨,極大的提升效能蔽介。
渲染對象樹中的每個(gè)對象都會在布局過程中接受父 對象的 Constraints 參數(shù),決定自己的大小, 然后父對象 就可以按照自己的邏輯決定各個(gè)子對象的位置,完成布局過程.
子對象不存儲自己在容器中的位置, 所以在它的位置發(fā)生改變時(shí)并不需要重新布局或者繪制. 子對象的位 置信息存儲在它自己的 parentData 字段中,但是該字段由它的父對象負(fù)責(zé)維護(hù),自身并不關(guān)心該字段的內(nèi)容煮寡。
同時(shí)也因?yàn)檫@種簡單的布局邏輯虹蓄, Flutter 可以在某些節(jié) 點(diǎn)設(shè)置布局邊界 (Relayout boundary), 即當(dāng)邊界內(nèi)的任 何對象發(fā)生重新布局時(shí)幸撕, 不會影響邊界外的對象薇组, 反之亦然.
參考資料
文中參考的資料如下