Flutter熱更新技術探索

2023-05-26 12:02:04

一,需求背景:

APP釋出到市場後,難免會遇到嚴重的BUG阻礙使用者使用,因此有在不釋出新版本APP的情況下使用熱更新技術立即修復BUG需求。原生APP(例如:Android & IOS)的熱更新需求已經比較成熟,但Flutter技術棧目前還缺少類似的技術方案,因此Flutter研發團隊,也需要類似的熱更新技術。

二,Flutter熱更新技術方向分析:

經過分析目前可能有三種可行的方案: 1)類似RN框架; 2)頁面動態元件框架; 3)Dart虛擬機器器客製化方案;

方案名稱 原理 優點 缺點 開源方案
類似RN的方案 用JS以Flutter語法寫dart,然後用JavaScript把XML DSL轉為Flutter的原子widget元件,然後再讓Flutter來渲染 由於ios系統內建支援js,ios上完全可以實現更新 1)由於跨語言執行,對於效能有影響;學習成本高 2)Android 端需要額外引入JS庫 手Q的MXFlutter,58同城的Fair
頁面動態元件方案 編譯期時插樁/預埋好DynamicWidget到程式碼中,然後動態下發Json 資料,通過協定好的語意匹配到JSON內的資料,動態替換Widget內容來實現更新 能支援Android/iOS 兩端的更新 1)UI更新相對較容易,業務邏輯動態化較麻煩; 2)語意解析器開發成本相對較大,且不易維護 3)需要一整套前後端服務和工具 天貓的Tangram,淘寶的DinamicX等
Dart虛擬機器器客製化方案 通過分析Dart虛擬機器器的原理,修改Flutter Engine層Java/C++程式碼實現熱更新的目標; 效能影響小,動態性很高,技術上可以替換所有Flutter頁面(包括UI,邏輯,資原始檔) 由於使用的是客製化引擎,需要維護不同版本的Flutter引擎程式碼; 未開源

因為其他方式都有開源的範例,本案將重點以第三種「Dart虛擬機器器客製化方案」為目標,做方案的研究講解。

三,預備知識

在開始瞭解技術方案之前,需要提前瞭解一些相應的技術概念:

3.1 Flutter編譯模式

Flutter開發語言是Dart,它的編譯模式來自Dart的編譯模式,主要有JIT(Just In Time)和AOT(Ahead Of Time)。

編譯模式名稱 特點 優點 缺點
JIT 即時編譯,典型例子V8,它可以即時編譯執行JS,只需要輸入原始碼字串,就可以編譯執行程式碼 可以動態下發和執行程式碼,不用管CPU架構,可以提供動態化內容 1,大量字串程式碼讓JIT編譯器花費時間和記憶體; 2,效能不好;
AOT 預先編譯,典型例子C/C++,通過GCC編譯成二進位制程式碼,然後安裝取得許可權後才可以載入執行 事先編譯好的,載入和執行速度快 1,編譯時區分CPU架構; 2,生成的二進位制程式碼包比較大; 3,二進位制程式碼需要取得許可權才可以執行,無法在ios系統上動態更新

Flutter編譯模式有:Debug,Release,Profile;

Flutter編譯模式 特點
Debug 對應JIT模式,支援裝置和模擬器; 開啟了斷言,支援快速開發,支援HotReload; 並未對包大小,執行速度做優化;
Release 對應AOT模式,支援真機,不支援模擬器; 禁止了所有斷言偵錯資訊; 對包大小,啟動和執行速度進行了優化;
Profile 類似Release模式,保留了一些偵錯功能,幫助效能分析;

3.2 Flutter編譯產物分析

Flutter下的iOS/Android工程本質上是一個標準的iOS/Android的工程;IOS平臺: Flutter通過在BuildPhase中新增shell(xcode_backend.sh)來生成和嵌入App.framework和Flutter.framework到ios; Android平臺: Flutter通過gradle來新增flutter.jar和編譯完的二進位制檔案新增到Android;

3.2.1 引擎層結構分析:

3.2.2 Android編譯產物的分析

3.2.3 IOS編譯產物的分析

四,熱更新技術方案分析

4.1 業務程式碼分析

根據「3.3.1」 ~「3.3.2」的分析可以確定無論是IOS還是Android APP業務程式碼都是由四個段組成:kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData、kDartIsolateSnapshotInstructions;理論上只要能動態替換載入的程式碼段&資料段程式碼即可實現目標。

名稱 註釋 作用 註釋
kDartIsolateSnapshotData Dart isolate資料段 類資訊,全域性變數,函數指標等 允許動態下發
kDartIsolateSnapshotInstructions Dart isolate指令段 包含由Dart isolate執行的AOT程式碼 IOS不允許動態下發
kDartVmSnapshotData vm isolate資料段 isolate 之間共用的 Dart 堆 (heap) 的初始狀態 允許動態下發
kDartVmSnapshotInstructions vm isolate指令段 包含 VM 中所有 Dart isolate 之間共用的通用程式的 AOT 指令 IOS不允許動態下發

註釋: isolate, snapshot, vm isolate含義解釋如下:

名稱 含義
isolate Dart是單執行緒,isolate跟執行緒差不多,可以理解為 Dart 中的執行緒。 isolate 與執行緒的區別:執行緒與執行緒之間是共用記憶體的,而 isolate 和 isolate 之間是記憶體不共用的。 不存在鎖競爭問題,兩個Isolate完全是兩條獨立的執行線,且每個Isolate都有自己的事件迴圈,它們之間只能通過傳送訊息通訊,所以它的資源開銷低於執行緒。
snapshot 將類資訊、全域性變數、函數指令直接以序列化的方式存在磁碟中,稱為 Snapshot(快照)。
vm isolate 同一個程序裡可以有很多isolate,但兩個 isolate 的堆區是不能共用的,所以官方設計了 VM isolate,也就是 kDartVmSnapshot,用來多個 isolate 之間的互動。

4.2 業務程式碼的載入分析(執行時)

按照4.1的分析思路,我們首先需要了解Flutter執行時程式碼載入的完整流程,經過梳理分析流程如下:

1 )Android- APP業務程式碼的載入流程:

2)IOS- APP業務程式碼的載入流程:

4.3 業務程式碼的編譯生成(編譯時)

根據以上的分析,我們知道了Flutter業務程式碼的資料結構,也知道了在執行時如何載入,因此我們只需要在編譯時做更改,產生自己需要的程式碼段,和資料段檔案。在執行時載入自己的構建產物即可達到目標。

1)在此以 IOS 構建自己的業務程式碼流程做詳細分析:

**有完成構建流程可以分析,基本流程是「Dart Code(業務程式碼)」 -> (通過Dart編譯器gen_snapshot.cc) 生成 snapshot_assemble.S 的組合檔案 -> (通過xcrun工具)生成 snapshot_assemble.o的obj檔案 -> (通過xcun clang工具鏈) 生成了 App.Framework。

2)Android的產物構建流程和IOS類似。由於Android有其他更簡單的方案, 因此省略詳細的構建流程分析,大致如下:

4.4 實現熱更新的方案探索

根據上面的技術分析結果,已經可以獨立生成自己的程式碼段,資料段檔案。通過需改虛擬機器器底層程式碼的方式,也可以動態的載入執行。但由於IOS系統目前底層的系統還不能動態載入可讀寫的程式碼段資料到記憶體中,所以還有技術難點需要突破。但Android端有更簡單的路徑可以解決,因此下面以Android端為例重點分析思路,大致如下圖所示:

由上圖可以得知,Android端 熱修復核心步驟如下:

1, 修改Flutter Engine程式碼,載入指定路徑的libapp.so和flutter_aasets,比如私有目錄(data/data/files);

2, 編譯APK時,利用Gradle Transform外掛,根據Flutter SDK的engine version動態替換官方的Flutter engine,最終寫入修改後的engine到APK;

3, 生成修補程式包:利用BSdiff演演算法比較新舊APK檔案,生成patch修補程式包

4, APP啟動時存取後端介面,根據引數(app的版本號,修補程式包版本號,md5,flutter SDK版本號,Engine版本號)拉取修補程式包;

5, 合成修補程式包:校驗md5,app版本號,修補程式版本號,安裝時間;

6, 自定義Flutter Engine載入指定路徑的libapp.so和flutter_assets資原始檔;

作者:京東科技 劉振中、周智

內容來源:京東雲開發者社群