深入理解flutter的編譯原理與優(yōu)化

摘要: 閑魚技術(shù)-正物 問題背景 對(duì)于開發(fā)者而言钠导,什么是Flutter略水?它是用什么語言編寫的郎汪,包含哪幾部分赤赊,是如何被編譯,運(yùn)行到設(shè)備上的呢怒竿?Flutter如何做到Debug模式Hot Reload快速生效變更砍鸠,Release模式原生體驗(yàn)的呢扩氢?Flutter工程和我們的Android/iOS工程有何差別耕驰,關(guān)...

閑魚技術(shù)-正物

問題背景

對(duì)于開發(fā)者而言,什么是Flutter录豺?它是用什么語言編寫的朦肘,包含哪幾部分,是如何被編譯双饥,運(yùn)行到設(shè)備上的呢媒抠?Flutter如何做到Debug模式Hot Reload快速生效變更,Release模式原生體驗(yàn)的呢咏花?Flutter工程和我們的Android/iOS工程有何差別趴生,關(guān)系如何,又是如何嵌入Android/iOS的呢昏翰?Flutter的渲染和事件傳遞機(jī)制如何工作苍匆?Flutter支持熱更新嗎?Flutter官方并未提供iOS下的armv7支持棚菊,確實(shí)如此嗎浸踩?在使用Flutter的時(shí)候,如果發(fā)現(xiàn)了engine的bug统求,如何去修改和生效检碗?構(gòu)建緩慢或出錯(cuò)又如何去定位,修改和生效呢码邻?

凡此種種折剃,都需要對(duì)Flutter從設(shè)計(jì),開發(fā)構(gòu)建像屋,到最終運(yùn)行有一個(gè)全局視角的觀察怕犁。

本文將以一個(gè)簡(jiǎn)單的hello_flutter為例,介紹下Flutter相關(guān)原理及定制與優(yōu)化。

Flutter簡(jiǎn)介

1645a5e314a8e573~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

Flutter的架構(gòu)主要分成三層:Framework因苹,Engine和Embedder苟耻。

Framework使用dart實(shí)現(xiàn),包括Material Design風(fēng)格的Widget,Cupertino(針對(duì)iOS)風(fēng)格的Widgets扶檐,文本/圖片/按鈕等基礎(chǔ)Widgets凶杖,渲染,動(dòng)畫款筑,手勢(shì)等智蝠。此部分的核心代碼是:flutter倉(cāng)庫下的flutter package,以及sky_engine倉(cāng)庫下的io,async,ui(dart:ui庫提供了Flutter框架和引擎之間的接口)等package奈梳。

Engine使用C++實(shí)現(xiàn)杈湾,主要包括:Skia,Dart和Text。Skia是開源的二維圖形庫攘须,提供了適用于多種軟硬件平臺(tái)的通用API漆撞。其已作為Google Chrome,Chrome OS于宙,Android, Mozilla Firefox, Firefox OS等其他眾多產(chǎn)品的圖形引擎浮驳,支持平臺(tái)還包括Windows7+,macOS 10.10.5+,iOS8+,Android4.1+,Ubuntu14.04+等。Dart部分主要包括:Dart Runtime捞魁,Garbage Collection(GC)至会,如果是Debug模式的話,還包括JIT(Just In Time)支持谱俭。Release和Profile模式下奉件,是AOT(Ahead Of Time)編譯成了原生的arm代碼,并不存在JIT部分昆著。Text即文本渲染县貌,其渲染層次如下:衍生自minikin的libtxt庫(用于字體選擇,分隔行)宣吱。HartBuzz用于字形選擇和成型窃这。Skia作為渲染/GPU后端,在Android和Fuchsia上使用FreeType渲染征候,在iOS上使用CoreGraphics來渲染字體杭攻。

Embedder是一個(gè)嵌入層,即把Flutter嵌入到各個(gè)平臺(tái)上去疤坝,這里做的主要工作包括渲染Surface設(shè)置,線程設(shè)置兆解,以及插件等。從這里可以看出跑揉,F(xiàn)lutter的平臺(tái)相關(guān)層很低锅睛,平臺(tái)(如iOS)只是提供一個(gè)畫布埠巨,剩余的所有渲染相關(guān)的邏輯都在Flutter內(nèi)部,這就使得它具有了很好的跨端一致性现拒。

Flutter工程結(jié)構(gòu)
本文使用開發(fā)環(huán)境為flutter beta v0.3.1辣垒,對(duì)應(yīng)的engine commit:09d05a389。

以hello_flutter工程為例印蔬,F(xiàn)lutter工程結(jié)構(gòu)如下所示:

1645a5e412ad585b~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

其中ios為iOS部分代碼勋桶,使用CocoaPods管理依賴,android為Android部分代碼侥猬,使用Gradle管理依賴例驹,lib為dart代碼,使用pub管理依賴退唠。類似iOS中Cocoapods對(duì)應(yīng)的Podfile和Podfile.lock鹃锈,pub下則是pubspec.yaml和pubspec.lock。

Flutter模式

對(duì)于Flutter瞧预,它支持常見的debug,release,profile等模式屎债,但它又有其不一樣。

Debug模式:對(duì)應(yīng)了Dart的JIT模式松蒜,又稱檢查模式或者慢速模式扔茅。支持設(shè)備,模擬器(iOS/Android)秸苗,此模式下打開了斷言,包括所有的調(diào)試信息运褪,服務(wù)擴(kuò)展和Observatory等調(diào)試輔助惊楼。此模式為快速開發(fā)和運(yùn)行做了優(yōu)化,但并未對(duì)執(zhí)行速度秸讹,包大小和部署做優(yōu)化檀咙。Debug模式下,編譯使用JIT技術(shù)璃诀,支持廣受歡迎的亞秒級(jí)有狀態(tài)的hot reload弧可。

Release模式:對(duì)應(yīng)了Dart的AOT模式,此模式目標(biāo)即為部署到終端用戶劣欢。只支持真機(jī)棕诵,不包括模擬器。關(guān)閉了所有斷言凿将,盡可能多地去掉了調(diào)試信息校套,關(guān)閉了所有調(diào)試工具。為快速啟動(dòng)牧抵,快速執(zhí)行笛匙,包大小做了優(yōu)化侨把。禁止了所有調(diào)試輔助手段,服務(wù)擴(kuò)展妹孙。

Profile模式:類似Release模式秋柄,只是多了對(duì)于Profile模式的服務(wù)擴(kuò)展的支持,支持跟蹤蠢正,以及最小化使用跟蹤信息需要的依賴华匾,例如,observatory可以連接上進(jìn)程机隙。Profile并不支持模擬器的原因在于蜘拉,模擬器上的診斷并不代表真實(shí)的性能。

鑒于Profile同Release在編譯原理等上無差異有鹿,本文只討論Debug和Release模式旭旭。

事實(shí)上flutter下的iOS/Android工程本質(zhì)上依然是一個(gè)標(biāo)準(zhǔn)的iOS/Android的工程,flutter只是通過在BuildPhase中添加shell來生成和嵌入App.framework和Flutter.framework(iOS),通過gradle來添加flutter.jar和vm/isolate_snapshot_data/instr(Android)來將Flutter相關(guān)代碼編譯和嵌入原生App而已葱跋。因此本文主要討論因flutter引入的構(gòu)建持寄,運(yùn)行等原理。編譯target雖然包括arm,x64,x86,arm64娱俺,但因原理類似稍味,本文只討論arm相關(guān)(如無特殊說明,android默認(rèn)為armv7)荠卷。

Flutter代碼的編譯與運(yùn)行(iOS)
Release模式下的編譯
release模式下模庐,flutter下iOS工程中dart代碼構(gòu)建鏈路如下所示:

1645a5e2e9902ffe~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

其中g(shù)en_snapshot是dart編譯器,采用了tree shaking(類似依賴樹邏輯油宜,可生成最小包掂碱,也因而在Flutter中禁止了dart支持的反射特性)等技術(shù),用于生成匯編形式的機(jī)器代碼,再通過xcrun等編譯工具鏈生成最終的App.framework慎冤。換句話說疼燥,所有的dart代碼,包括業(yè)務(wù)代碼蚁堤,三方package代碼醉者,它們所依賴的flutter框架代碼,最終將會(huì)變成App.framework披诗。

tree shaking功能位于gen_snapshot中撬即,對(duì)應(yīng)邏輯參見: engine/src/third_party/dart/runtime/vm/compiler/aot/precompiler.cc

dart代碼最終對(duì)應(yīng)到App.framework中的符號(hào)如下所示:

1645a5e437782ab6~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

事實(shí)上,類似Android Release下的產(chǎn)物(見下文)藤巢,App.framework也包含了kDartVmSnapshotData搞莺,kDartVmSnapshotInstructions,kDartIsolateSnapshotData掂咒,kDartIsolateSnapshotInstructions四個(gè)部分才沧。為什么iOS使用App.framework這種方式迈喉,而不是Android的四個(gè)文件的方式呢?原因在于在iOS下温圆,因?yàn)橄到y(tǒng)的限制挨摸,F(xiàn)lutter引擎不能夠在運(yùn)行時(shí)將某內(nèi)存頁標(biāo)記為可執(zhí)行,而Android是可以的岁歉。

Flutter.framework對(duì)應(yīng)了Flutter架構(gòu)中的engine部分得运,以及Embedder。實(shí)際中Flutter.framework位于flutter倉(cāng)庫的/bin/cache/artifacts/engine/ios*下锅移,默認(rèn)從google倉(cāng)庫拉取熔掺。當(dāng)需要自定義修改的時(shí)候,可通過下載engine源碼非剃,利用Ninja構(gòu)建系統(tǒng)來生成置逻。

Flutter相關(guān)代碼的最終產(chǎn)物是:App.framework(dart代碼生成)和Flutter.framework(引擎)。從Xcode工程的視角看备绽,Generated.xcconfig描述了Flutter相關(guān)環(huán)境的配置信息券坞,然后Runner工程設(shè)置中的Build Phases新增的xcode_backend.sh實(shí)現(xiàn)了Flutter.framework的拷貝(從Flutter倉(cāng)庫的引擎到Runner工程根目錄下的Flutter目錄)與嵌入和App.framework的編譯與嵌入。最終生成的Runner.app中Flutter相關(guān)內(nèi)容如下所示:

1645a5e429d649ec~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

其中flutter_assets是相關(guān)的資源肺素,代碼則是位于Frameworks下的App.framework和Flutter.framework恨锚。

Debug模式下的編譯
Debug模式下flutter的編譯,結(jié)構(gòu)類似Release模式倍靡,差異主要表現(xiàn)為兩點(diǎn):

1.Flutter.framework

因?yàn)槭荄ebug猴伶,此模式下Framework中是有JIT支持的,而在Release模式下并沒有JIT部分菌瘫。

2.App.framework

不同于AOT模式下的App.framework是Dart代碼對(duì)應(yīng)的本地機(jī)器代碼蜗顽,JIT模式下,App.framework只有幾個(gè)簡(jiǎn)單的API雨让,其Dart代碼存在于snapshot_blob.bin文件里。這部分的snapshot是腳本快照忿等,里面是簡(jiǎn)單的標(biāo)記化的源代碼栖忠。所有的注釋,空白字符都被移除贸街,常量也被規(guī)范化庵寞,也沒有機(jī)器碼,tree shaking或者是混淆薛匪。

App.framework中的符號(hào)表如下所示:

1645a5e412ad585b~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

對(duì)Runner.app/flutter_assets/snapshot_blob.bin執(zhí)行strings命令可以看到如下內(nèi)容:

1645a5e40635c97c~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

Debug模式下main入口的調(diào)用堆棧如下:

debug isolate main callstack

Flutter代碼的編譯與運(yùn)行(Android)
鑒于Android和iOS除了部分平臺(tái)相關(guān)的特性外捐川,其他邏輯如Release對(duì)應(yīng)AOT,Debug對(duì)應(yīng)JIT等均類似逸尖,此處只涉及兩者不同古沥。

Release模式下的編譯
release模式下瘸右,flutter下Android工程中dart代碼整個(gè)構(gòu)建鏈路如下所示:

1645a5e2f26ea304~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

其中vm/isolate_snapshot_data/instr內(nèi)容均為arm指令,將會(huì)在運(yùn)行時(shí)被engine載入岩齿,并標(biāo)記vm/isolate_snapshot_instr為可執(zhí)行太颤。vm_中涉及runtime等服務(wù)(如gc),用于初始化DartVM盹沈,調(diào)用入口見Dart_Initialize(dart_api.h)龄章。isolate__則是對(duì)應(yīng)了我們的App代碼,用于創(chuàng)建一個(gè)新的isolate,調(diào)用入口見Dart_CreateIsolate(dart_api.h)乞封。flutter.jar類似iOS的Flutter.framework做裙,包括了engine部分的代碼(Flutter.jar中的libflutter.so),以及一套將Flutter嵌入Android的類和接口(FlutterMain,FlutterView,FlutterNativeView等)肃晚。實(shí)際中flutter.jar位于flutter倉(cāng)庫的/bin/cache/artifacts/engine/android*下锚贱,默認(rèn)從google倉(cāng)庫拉取。當(dāng)需要自定義修改的時(shí)候陷揪,可通過下載engine源碼惋鸥,利用Ninja構(gòu)建系統(tǒng)來生成flutter.jar。

原文鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悍缠,一起剝皮案震驚了整個(gè)濱河市卦绣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌飞蚓,老刑警劉巖滤港,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異趴拧,居然都是意外死亡溅漾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門著榴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來添履,“玉大人,你說我怎么就攤上這事脑又∧弘剩” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵问麸,是天一觀的道長(zhǎng)往衷。 經(jīng)常有香客問我,道長(zhǎng)严卖,這世上最難降的妖魔是什么席舍? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮哮笆,結(jié)果婚禮上来颤,老公的妹妹穿的比我還像新娘汰扭。我一直安慰自己,他們只是感情好脚曾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布东且。 她就那樣靜靜地躺著,像睡著了一般本讥。 火紅的嫁衣襯著肌膚如雪珊泳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天拷沸,我揣著相機(jī)與錄音色查,去河邊找鬼。 笑死撞芍,一個(gè)胖子當(dāng)著我的面吹牛秧了,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播序无,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼验毡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了帝嗡?” 一聲冷哼從身側(cè)響起晶通,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哟玷,沒想到半個(gè)月后狮辽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巢寡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年喉脖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抑月。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡树叽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谦絮,到底是詐尸還是另有隱情菱皆,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布挨稿,位于F島的核電站,受9級(jí)特大地震影響京痢,放射性物質(zhì)發(fā)生泄漏奶甘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一祭椰、第九天 我趴在偏房一處隱蔽的房頂上張望臭家。 院中可真熱鬧疲陕,春花似錦、人聲如沸钉赁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽你踩。三九已至诅岩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間带膜,已是汗流浹背吩谦。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膝藕,地道東北人式廷。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像芭挽,于是被迫代替她去往敵國(guó)和親滑废。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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