原文鏈接 https://www.stephenw.cc/2018/07/30/flutter-compile-mode/
Flutter的編譯模式
Jul 30, 2018
使用 Flutter構(gòu)建過(guò) App的人一定有一個(gè)困惑杰扫,就是 Flutter編譯出的產(chǎn)物到底是什么玩意增拥,有時(shí)候分為幾個(gè)文件,有時(shí)候是一個(gè)動(dòng)態(tài)庫(kù)具温,真的叫人摸不著頭腦薪韩。
本文詳細(xì)解釋一下 Flutter的編譯模式穷蛹。
編譯模式的分類.
編程語(yǔ)言要達(dá)到可運(yùn)行的目的需要經(jīng)過(guò)編譯偶惠,一般地來(lái)說(shuō),編譯模式分為兩類:JIT 和 AOT光坝。
JIT:
JIT全稱 Just In Time(即時(shí)編譯)尸诽,典型的例子就是 v8,它可以即時(shí)編譯并運(yùn)行 JavaScript盯另。所以你只需要輸入源代碼字符串性含,v8就可以幫你編譯并運(yùn)行代碼。通常來(lái)說(shuō)鸳惯,支持 JIT的語(yǔ)言一般能夠支持自省函數(shù)(eval)商蕴,在運(yùn)行時(shí)動(dòng)態(tài)地執(zhí)行代碼。
JIT模式的優(yōu)勢(shì)是顯而易見的悲敷,可以動(dòng)態(tài)下發(fā)和執(zhí)行代碼究恤,而不用管用戶的機(jī)器是什么架構(gòu),為應(yīng)用的用戶提供豐富而動(dòng)態(tài)地內(nèi)容后德。
但 JIT的劣勢(shì)也是顯而易見的部宿,大量字符串的代碼很容易讓 JIT編譯器花費(fèi)很多時(shí)間和內(nèi)存進(jìn)行編譯,給用戶帶來(lái)的直接感受就是應(yīng)用啟動(dòng)慢瓢湃。
AOT:
AOT全稱 Ahead Of Time(事前編譯)理张,典型的例子就是 C/C++,LLVM或 GCC通過(guò)編譯并生成 C/C++的二進(jìn)制代碼绵患,然后這些二進(jìn)制通過(guò)用戶安裝并取得執(zhí)行權(quán)限后才可以通過(guò)進(jìn)程加載執(zhí)行雾叭。
AOT的優(yōu)勢(shì)也是顯而易見的,事先編譯好的二進(jìn)制代碼落蝙,加載和執(zhí)行的速度都會(huì)非持快暂幼。(所以編程語(yǔ)言速度排行榜上前列都是 AOT編譯類語(yǔ)言)這樣的速度可以在密集計(jì)算場(chǎng)景下給用戶帶來(lái)非常好的體驗(yàn),比如大型游戲的引擎渲染和邏輯執(zhí)行移迫。
但是 AOT的劣勢(shì)也是顯而易見的旺嬉,編譯需要區(qū)分用戶機(jī)器的架構(gòu),生成不同架構(gòu)的二進(jìn)制代碼厨埋,除了架構(gòu)邪媳,二進(jìn)制代碼本身也會(huì)讓用戶下載的安裝包比較大。二進(jìn)制代碼一般需要取得執(zhí)行權(quán)限才可以執(zhí)行荡陷,所以無(wú)法在權(quán)限比較嚴(yán)格的系統(tǒng)中進(jìn)行動(dòng)態(tài)更新(如 iOS)雨效。
Dart的編譯模式
Flutter使用 Dart作為編程語(yǔ)言,自然其編譯模式也脫離不了 Dart的干系废赞。首先我們需要了解一下 Dart所支持的編譯模式徽龟。
- Script:最普通的 JIT模式,在 PC命令行調(diào)用 dart vm執(zhí)行 dart源代碼文件即是這種模式蛹头。
- Script Snapshot:JIT模式顿肺,和上一個(gè)不同的是戏溺,這里載入的是已經(jīng) token化的 dart源代碼渣蜗,提前執(zhí)行了上一步的 lexer步驟。
- Application Snapshot:JIT模式旷祸,這種模式來(lái)源于 dart vm直接載入源碼后 dump出數(shù)據(jù)耕拷。dart vm通過(guò)這種數(shù)據(jù)啟動(dòng)會(huì)更快。不過(guò)值得一提的是這種模式是區(qū)分架構(gòu)的托享,在 x64上生成的數(shù)據(jù)不可以給 arm使用骚烧。
- AOT:AOT模式,直接將 dart源碼編譯出 .S文件闰围,然后通過(guò)匯編器生成對(duì)應(yīng)架構(gòu)的代碼赃绊。
總結(jié)一下剛才的列表,可以發(fā)現(xiàn):
模式/比較項(xiàng) | 編譯模式 | 區(qū)分架構(gòu) | 打包大小 | 動(dòng)態(tài)化 | |
---|---|---|---|---|---|
Script | JIT | 否 | 小 | 是 | |
Script Snapshot | JIT | 否 | 很小 | 是 | |
Application Snapshot | JIT | 是 | 比較大 | 是(注意架構(gòu)) | |
AOT | AOT | 是 | 比較大 | 否 |
Flutter的編譯模式
Flutter 完全采用了 Dart羡榴,按道理來(lái)說(shuō)編譯模式一致才是碧查,但是事實(shí)并不是這樣。由于 Android和 iOS平臺(tái)的生態(tài)差異校仑,F(xiàn)lutter也衍生除了非常豐富的編譯模式忠售。
- Script:同 Dart Script模式一致,雖然 Flutter支持迄沫,但暫未看到使用稻扬,畢竟影響啟動(dòng)速度。
- Script Snapshot:同 Dart Script Snapshot一致羊瘩,同樣支持但未使用泰佳,F(xiàn)lutter有大量的視圖渲染邏輯盼砍,純 JIT模式影響執(zhí)行速度。
- Kernel Snapshot:Dart的 bytecode 模式逝她,與 Application Snapshot不同衬廷,bytecode模式是不區(qū)分架構(gòu)的。 Kernel Snapshot在 Flutter項(xiàng)目?jī)?nèi)也叫 Core Snapshot汽绢。bytecode模式可以歸類為 AOT編譯吗跋。
- Core JIT:Dart的一種二進(jìn)制模式,將指令代碼和 heap數(shù)據(jù)打包成文件宁昭,然后在 vm和 isolate啟動(dòng)時(shí)載入跌宛,直接標(biāo)記內(nèi)存可執(zhí)行,可以說(shuō)這是一種 AOT模式积仗。Core JIT也被叫做 AOTBlob
- AOT Assembly: 即 Dart的 AOT模式疆拘。直接生成匯編源代碼文件,由各平臺(tái)自行匯編寂曹。
可以看出來(lái)哎迄,F(xiàn)lutter將 Dart的編譯模式復(fù)雜化了,多了不少概念隆圆,要一下敘述清楚是比較困難的漱挚,所以我們著重從 Flutter應(yīng)用開發(fā)的各個(gè)階段來(lái)解讀。
開發(fā)階段的編譯模式
在開發(fā)階段渺氧,我們需要 Flutter的 Hot Reload和 Hot Restart功能旨涝,方便 UI快速成型。同時(shí)侣背,框架層也需要比較高的性能來(lái)進(jìn)行視圖渲染展現(xiàn)白华。因此開發(fā)模式下,F(xiàn)lutter使用了 Kernel Snapshot模式編譯贩耐。
在打包產(chǎn)物中弧腥,你將發(fā)現(xiàn)幾樣?xùn)|西:
- isolate_snapshot_data:用于加速 isolate啟動(dòng),業(yè)務(wù)無(wú)關(guān)代碼潮太,固定管搪,僅和 flutter engine版本有關(guān)
- platform.dill:和 dart vm相關(guān)的 kernel代碼,僅和 dart版本以及 engine編譯版本有關(guān)消别。固定抛蚤,業(yè)務(wù)無(wú)關(guān)代碼。
- vm_snapshot_data: 用于加速 dart vm啟動(dòng)的產(chǎn)物寻狂,業(yè)務(wù)無(wú)關(guān)代碼岁经,僅和 flutter engine版本有關(guān)
- kernel_blob.bin:業(yè)務(wù)代碼產(chǎn)物
項(xiàng)目/平臺(tái) | Android | iOS | |
---|---|---|---|
代碼環(huán)境 | debug | debug | |
編譯模式 | Kernel Snapshot | Kernel Snapshot | |
打包工具 | dart vm (2.0) | dart vm (2.0) | |
Flutter命令 | flutter build bundle | flutter build bundle | |
打包產(chǎn)物 | flutter_assets/* | flutter_assets/* |
生產(chǎn)階段的編譯模式
在生產(chǎn)階段,應(yīng)用需要的是非成呷快的速度缀壤,所以 Android和 iOS target毫無(wú)意外地都選擇了 AOT打包樊拓。不過(guò)由于平臺(tái)特性不同,打包模式也是天壤之別塘慕。
項(xiàng)目/平臺(tái) | Android | iOS | Android(–build-shared-library) |
---|---|---|---|
代碼環(huán)境 | release | release | release |
編譯模式 | Core JIT | AOT Assembly | AOT Assembly |
打包工具 | gen_snapshot | gen_snapshot | gen_snapshot |
Flutter命令 | flutter build aot | flutter build aot –ios | flutter build aot –build-shared-library |
打包產(chǎn)物 | flutter_assets/* | App.framework | app.so |
首先我們很容易認(rèn)識(shí)到 iOS平臺(tái)上做法的原因:App Store審核條例不允許動(dòng)態(tài)下發(fā)可執(zhí)行二進(jìn)制代碼筋夏。
所以在 iOS上,除了 JavaScript图呢,其他語(yǔ)言運(yùn)行時(shí)的實(shí)現(xiàn)都選擇了 AOT条篷。(比如 OpenJDK在 iOS實(shí)現(xiàn)就是 AOT)
在 Android上,F(xiàn)lutter的做法有點(diǎn)意思:支持了兩種不同的路子蛤织。
Core JIT的打包產(chǎn)物有 4個(gè):isolate_snapshot_data, vm_snapshot_data, isolate_snapshot_instr, vm_snapshot_instr. 我們不認(rèn)識(shí)的產(chǎn)物只有 2個(gè):isolate_snapshot_instr和 vm_snapshot_instr赴叹,其實(shí)它倆代表著 vm和 isolate啟動(dòng)后所承載的指令等數(shù)據(jù),在載入后指蚜,直接將該塊內(nèi)存執(zhí)行即可乞巧。
Android的 AOT Assembly打包方式很容易讓人想到需要支持多架構(gòu),無(wú)疑增大了代碼包摊鸡,并且該處代碼需要從 JNI調(diào)用绽媒,遠(yuǎn)不如 Core JIT的 Java API方便。所以 Android上默認(rèn)使用 Core JIT打包免猾,而不是 AOT Assembly是辕。
Flutter Engine對(duì)編譯模式的支持
在我的上篇文章:Flutter原理簡(jiǎn)解中提到,engine承載了 dart運(yùn)行時(shí)掸刊,毫無(wú)疑問(wèn) engine需要和打包出來(lái)的代碼對(duì)的上號(hào)才行免糕。
在 engine的編譯模式中,F(xiàn)lutter是這樣選擇的:
項(xiàng)目/平臺(tái) | iOS | Android | |
---|---|---|---|
Script | 不支持 | 不支持 | |
ScriptSnapshot | 理論支持 | 理論支持 | |
Kernel Snapshot | 支持忧侧,runmode = dynamic | 支持,runmode = dynamic | |
CoreJIT | 不支持 | 支持 | |
AOT Assembly | 支持 | 支持 |
所以我們可以看到牌芋,F(xiàn)lutter的編譯模式是完全根據(jù) Engine的支持度來(lái)設(shè)計(jì)的蚓炬。
結(jié)論
看到這里,我們完全可以得出一個(gè)結(jié)論:Flutter是一種高性能的躺屁、可跨平臺(tái)的肯夏、動(dòng)態(tài)化應(yīng)用開發(fā)方案。
在 iOS和 Android平臺(tái)上犀暑,動(dòng)態(tài)化完全可由 Kernel Snapshot打包實(shí)現(xiàn)驯击,并且產(chǎn)物是一致通用的。不過(guò)目前通過(guò)打包工具進(jìn)行了閹割耐亏,只能生成 debug產(chǎn)物徊都。
并且如果不需要?jiǎng)討B(tài)化,同樣可以打包出擁有更高執(zhí)行性能的二進(jìn)制庫(kù)文件使用广辰。這個(gè)特性目前就已經(jīng)支持
有了理論的支持暇矫,我們就可以著手做改造的事了主之。