一,需求背景:
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資源檔案;
作者:京東科技 劉振中、周智
內容來源:京東云開發者社區
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/553485.html
標籤:其他
下一篇:返回列表
