Flutter 高性能原理淺析

這是我第三篇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框架

image

從圖中可以看出 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ì)

image

看Hybrid的架構(gòu)伸眶,我們可以知道UI層的渲染是基于Webview去渲染惊窖,他的性能取決于webview的渲染性能,目前已知webview渲染性能 < NativeUI的性能

image

RN/Weex 的架構(gòu)中厘贼,是基于Native的UI框架去適配界酒,中間多了一層js轉(zhuǎn)NativeUI的過程

image

而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)存段的過程

image

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)存如圖.

image

整個(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ì)列中的消息轧飞。

image

有了消息隊(duì)列,然后有了循環(huán)去讀取消息隊(duì)列中的消息撒踪,就可以有單線程去執(zhí)行異步消息的能力.
一般的消息使用dart:async中使用Future來支持異步消息.

Flutter Engine 高性能

在講Flutter Engin層時(shí)过咬,我們先講一下屏幕繪制的原理.

屏幕繪制原理

image

我們都知道顯示器以固定的頻率刷新,比如 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ī)制

image

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樹原理

image

在 Flutter 界面渲染過程分為 3 個(gè)階段: 布局抚垄、繪制蜕窿、合成.

而布局階段,有三個(gè)重要的對象.RenderObject呆馁、Element渠羞、Widget.

image
  • 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è)布局過程中只需要深度遍歷一次次氨,極大的提升效能蔽介。

image

渲染對象樹中的每個(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í)幸撕, 不會影響邊界外的對象薇组, 反之亦然.

參考資料

文中參考的資料如下

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市坐儿,隨后出現(xiàn)的幾起案子律胀,更是在濱河造成了極大的恐慌,老刑警劉巖貌矿,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炭菌,死亡現(xiàn)場離奇詭異,居然都是意外死亡逛漫,警方通過查閱死者的電腦和手機(jī)黑低,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尽楔,“玉大人投储,你說我怎么就攤上這事±觯” “怎么了玛荞?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呕寝。 經(jīng)常有香客問我勋眯,道長,這世上最難降的妖魔是什么下梢? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任客蹋,我火速辦了婚禮,結(jié)果婚禮上孽江,老公的妹妹穿的比我還像新娘讶坯。我一直安慰自己,他們只是感情好岗屏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布辆琅。 她就那樣靜靜地躺著,像睡著了一般这刷。 火紅的嫁衣襯著肌膚如雪婉烟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天暇屋,我揣著相機(jī)與錄音似袁,去河邊找鬼。 笑死咐刨,一個(gè)胖子當(dāng)著我的面吹牛昙衅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播定鸟,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼绒尊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仔粥?” 一聲冷哼從身側(cè)響起婴谱,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躯泰,沒想到半個(gè)月后谭羔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡麦向,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年瘟裸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诵竭。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡话告,死狀恐怖兼搏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沙郭,我是刑警寧澤佛呻,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站病线,受9級特大地震影響吓著,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜送挑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一绑莺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惕耕,春花似錦纺裁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惭缰,卻和暖如春浪南,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漱受。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工络凿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昂羡。 一個(gè)月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓絮记,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虐先。 傳聞我的和親對象是個(gè)殘疾皇子怨愤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容