深入理解flutter的編譯原理與優化

摘要:閑魚技術-正物 問題背景 對于開發者而言,什么是Flutter?它是用什么語言編寫的,包含哪幾部分,是如何被編譯,運行到設備上的呢?Flutter如何做到Debug模式Hot Reload快速生效變更,Release模式原生體驗的呢?Flutter工程和我們的Android/iOS工程有何差別,關...

閑魚技術-正物

問題背景

對于開發者而言,什么是Flutter?它是用什么語言編寫的,包含哪幾部分,是如何被編譯,運行到設備上的呢?Flutter如何做到Debug模式Hot Reload快速生效變更,Release模式原生體驗的呢?Flutter工程和我們的Android/iOS工程有何差別,關系如何,又是如何嵌入Android/iOS的呢?Flutter的渲染和事件傳遞機制如何工作?Flutter支持熱更新嗎?Flutter官方并未提供iOS下的armv7支持,確實如此嗎?在使用Flutter的時候,如果發現了engine的bug,如何去修改和生效?構建緩慢或出錯又如何去定位,修改和生效呢?

凡此種種,都需要對Flutter從設計,開發構建,到最終運行有一個全局視角的觀察。

本文將以一個簡單的hello_flutter為例,介紹下Flutter相關原理及定制與優化。

Flutter簡介

Flutter的架構主要分成三層:Framework,Engine和Embedder。

Framework使用dart實現,包括Material Design風格的Widget,Cupertino(針對iOS)風格的Widgets,文本/圖片/按鈕等基礎Widgets,渲染,動畫,手勢等。此部分的核心代碼是:flutter倉庫下的flutter package,以及sky_engine倉庫下的io,async,ui(dart:ui庫提供了Flutter框架和引擎之間的接口)等package。

Engine使用C++實現,主要包括:Skia,Dart和Text。Skia是開源的二維圖形庫,提供了適用于多種軟硬件平臺的通用API。其已作為Google Chrome,Chrome OS,Android, Mozilla Firefox, Firefox OS等其他眾多產品的圖形引擎,支持平臺還包括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是一個嵌入層,即把Flutter嵌入到各個平臺上去,這里做的主要工作包括渲染Surface設置,線程設置,以及插件等。從這里可以看出,Flutter的平臺相關層很低,平臺(如iOS)只是提供一個畫布,剩余的所有渲染相關的邏輯都在Flutter內部,這就使得它具有了很好的跨端一致性。

Flutter工程結構

本文使用開發環境為flutter beta v0.3.1,對應的engine commit:09d05a389。

以hello_flutter工程為例,Flutter工程結構如下所示:

其中ios為iOS部分代碼,使用CocoaPods管理依賴,android為Android部分代碼,使用Gradle管理依賴,lib為dart代碼,使用pub管理依賴。類似iOS中Cocoapods對應的Podfile和Podfile.lock,pub下則是pubspec.yaml和pubspec.lock。

Flutter模式

對于Flutter,它支持常見的debug,release,profile等模式,但它又有其不一樣。

Debug模式:對應了Dart的JIT模式,又稱檢查模式或者慢速模式。支持設備,模擬器(iOS/Android),此模式下打開了斷言,包括所有的調試信息,服務擴展和Observatory等調試輔助。此模式為快速開發和運行做了優化,但并未對執行速度,包大小和部署做優化。Debug模式下,編譯使用JIT技術,支持廣受歡迎的亞秒級有狀態的hot reload。

Release模式:對應了Dart的AOT模式,此模式目標即為部署到終端用戶。只支持真機,不包括模擬器。關閉了所有斷言,盡可能多地去掉了調試信息,關閉了所有調試工具。為快速啟動,快速執行,包大小做了優化。禁止了所有調試輔助手段,服務擴展。

Profile模式:類似Release模式,只是多了對于Profile模式的服務擴展的支持,支持跟蹤,以及最小化使用跟蹤信息需要的依賴,例如,observatory可以連接上進程。Profile并不支持模擬器的原因在于,模擬器上的診斷并不代表真實的性能。

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

事實上flutter下的iOS/Android工程本質上依然是一個標準的iOS/Android的工程,flutter只是通過在BuildPhase中添加shell來生成和嵌入App.framework和Flutter.framework(iOS),通過gradle來添加flutter.jar和vm/isolate_snapshot_data/instr(Android)來將Flutter相關代碼編譯和嵌入原生App而已。因此本文主要討論因flutter引入的構建,運行等原理。編譯target雖然包括arm,x64,x86,arm64,但因原理類似,本文只討論arm相關(如無特殊說明,android默認為armv7)。

Flutter代碼的編譯與運行(iOS)

Release模式下的編譯

release模式下,flutter下iOS工程中dart代碼構建鏈路如下所示:

其中gen_snapshot是dart編譯器,采用了tree shaking(類似依賴樹邏輯,可生成最小包,也因而在Flutter中禁止了dart支持的反射特性)等技術,用于生成匯編形式的機器代碼,再通過xcrun等編譯工具鏈生成最終的App.framework。換句話說,所有的dart代碼,包括業務代碼,三方package代碼,它們所依賴的flutter框架代碼,最終將會變成App.framework。

tree shaking功能位于gen_snapshot中,對應邏輯參見: engine/src/third_party/dart/runtime/vm/compiler/aot/precompiler.cc

事實上,類似Android Release下的產物(見下文),App.framework也包含了kDartVmSnapshotData,kDartVmSnapshotInstructions,kDartIsolateSnapshotData,kDartIsolateSnapshotInstructions四個部分。為什么iOS使用App.framework這種方式,而不是Android的四個文件的方式呢?原因在于在iOS下,因為系統的限制,Flutter引擎不能夠在運行時將某內存頁標記為可執行,而Android是可以的。

Flutter.framework對應了Flutter架構中的engine部分,以及Embedder。實際中Flutter.framework位于flutter倉庫的/bin/cache/artifacts/engine/ios*下,默認從google倉庫拉取。當需要自定義修改的時候,可通過下載engine源碼,利用Ninja構建系統來生成。

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

其中flutter_assets是相關的資源,代碼則是位于Frameworks下的App.framework和Flutter.framework。

Release模式下的運行

Flutter相關的渲染,事件,通信處理邏輯如下所示:

其中dart中的main函數調用棧如下:

Debug模式下的編譯

Debug模式下flutter的編譯,結構類似Release模式,差異主要表現為兩點:

1.Flutter.framework

因為是Debug,此模式下Framework中是有JIT支持的,而在Release模式下并沒有JIT部分。

2.App.framework

不同于AOT模式下的App.framework是Dart代碼對應的本地機器代碼,JIT模式下,App.framework只有幾個簡單的API,其Dart代碼存在于snapshot_blob.bin文件里。這部分的snapshot是腳本快照,里面是簡單的標記化的源代碼。所有的注釋,空白字符都被移除,常量也被規范化,也沒有機器碼,tree shaking或者是混淆。

App.framework中的符號表如下所示:

對Runner.app/flutter_assets/snapshot_blob.bin執行strings命令可以看到如下內容:

Debug模式下main入口的調用堆棧如下:

Flutter代碼的編譯與運行(Android)

鑒于Android和iOS除了部分平臺相關的特性外,其他邏輯如Release對應AOT,Debug對應JIT等均類似,此處只涉及兩者不同。

Release模式下的編譯

release模式下,flutter下Android工程中dart代碼整個構建鏈路如下所示:

其中vm/isolate_snapshot_data/instr內容均為arm指令,將會在運行時被engine載入,并標記vm/isolate_snapshot_instr為可執行。vm_中涉及runtime等服務(如gc),用于初始化DartVM,調用入口見Dart_Initialize(dart_api.h)。isolate__則是對應了我們的App代碼,用于創建一個新的isolate,調用入口見Dart_CreateIsolate(dart_api.h)。flutter.jar類似iOS的Flutter.framework,包括了engine部分的代碼(Flutter.jar中的libflutter.so),以及一套將Flutter嵌入Android的類和接口(FlutterMain,FlutterView,FlutterNativeView等)。實際中flutter.jar位于flutter倉庫的/bin/cache/artifacts/engine/android*下,默認從google倉庫拉取。當需要自定義修改的時候,可通過下載engine源碼,利用Ninja構建系統來生成flutter.jar。

以isolate_snapshot_data/instr為例,執行disarm命令結果如下:

)

其Apk結構如下所示:

APK新安裝之后,會根據一個ts的判斷(packageinfo中的versionCode結合lastUpdateTime)來決定是否拷貝APK中的assets,拷貝后內容如下所示:

isolate/vm_snapshot_data/instr均最后位于app的本地data目錄下,而這部分又屬于可寫內容,因此可以通過下載并替換的方式,完成App的整個替換和更新。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容