APP發(fā)布到市場(chǎng)后,難免會(huì)遇到嚴(yán)重的BUG阻礙用戶使用,因此有在不發(fā)布新版本APP的情況下使用熱更新技術(shù)立即修復(fù)BUG需求。原生APP(例如:Android & IOS)的熱更新需求已經(jīng)比較成熟,但Flutter技術(shù)棧目前還缺少類似的技術(shù)方案,因此Flutter研發(fā)團(tuán)隊(duì),也需要類似的熱更新技術(shù)。
二,F(xiàn)lutter熱更新技術(shù)方向分析:經(jīng)過(guò)分析目前可能有三種可行的方案: 1)類似RN框架; 2)頁(yè)面動(dòng)態(tài)組件框架; 3)Dart虛擬機(jī)定制方案;
【資料圖】
方案名稱 | 原理 | 優(yōu)點(diǎn) | 缺點(diǎn) | 開源方案 |
---|---|---|---|---|
類似RN的方案 | 用JS以Flutter語(yǔ)法寫dart,然后用JavaScript把XML DSL轉(zhuǎn)為Flutter的原子widget組件,然后再讓Flutter來(lái)渲染 | 由于ios系統(tǒng)內(nèi)置支持js,ios上完全可以實(shí)現(xiàn)更新 | 1)由于跨語(yǔ)言執(zhí)行,對(duì)于性能有影響;學(xué)習(xí)成本高 2)Android 端需要額外引入JS庫(kù) | 手Q的MXFlutter,58同城的Fair |
頁(yè)面動(dòng)態(tài)組件方案 | 編譯期時(shí)插樁/預(yù)埋好DynamicWidget到代碼中,然后動(dòng)態(tài)下發(fā)Json 數(shù)據(jù),通過(guò)協(xié)定好的語(yǔ)義匹配到JSON內(nèi)的數(shù)據(jù),動(dòng)態(tài)替換Widget內(nèi)容來(lái)實(shí)現(xiàn)更新 | 能支持Android/iOS 兩端的更新 | 1)UI更新相對(duì)較容易,業(yè)務(wù)邏輯動(dòng)態(tài)化較麻煩; 2)語(yǔ)義解析器開發(fā)成本相對(duì)較大,且不易維護(hù) 3)需要一整套前后端服務(wù)和工具 | 天貓的Tangram,淘寶的DinamicX等 |
Dart虛擬機(jī)定制方案 | 通過(guò)分析Dart虛擬機(jī)的原理,修改Flutter Engine層Java/C++代碼實(shí)現(xiàn)熱更新的目標(biāo); | 性能影響小,動(dòng)態(tài)性很高,技術(shù)上可以替換所有Flutter頁(yè)面(包括UI,邏輯,資源文件) | 由于使用的是定制引擎,需要維護(hù)不同版本的Flutter引擎代碼; | 未開源 |
因?yàn)槠渌绞蕉加虚_源的示例,本案將重點(diǎn)以第三種“Dart虛擬機(jī)定制方案”為目標(biāo),做方案的研究講解。
三,預(yù)備知識(shí)在開始了解技術(shù)方案之前,需要提前了解一些相應(yīng)的技術(shù)概念:
3.1 Flutter編譯模式Flutter開發(fā)語(yǔ)言是Dart,它的編譯模式來(lái)自Dart的編譯模式,主要有JIT(Just In Time)和AOT(Ahead Of Time)。
編譯模式名稱 | 特點(diǎn) | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|---|
JIT | 即時(shí)編譯,典型例子V8,它可以即時(shí)編譯運(yùn)行JS,只需要輸入源代碼字符串,就可以編譯運(yùn)行代碼 | 可以動(dòng)態(tài)下發(fā)和執(zhí)行代碼,不用管CPU架構(gòu),可以提供動(dòng)態(tài)化內(nèi)容 | 1,大量字符串代碼讓JIT編譯器花費(fèi)時(shí)間和內(nèi)存; 2,性能不好; |
AOT | 預(yù)先編譯,典型例子C/C++,通過(guò)GCC編譯成二進(jìn)制代碼,然后安裝取得權(quán)限后才可以加載執(zhí)行 | 事先編譯好的,加載和執(zhí)行速度快 | 1,編譯時(shí)區(qū)分CPU架構(gòu); 2,生成的二進(jìn)制代碼包比較大; 3,二進(jìn)制代碼需要取得權(quán)限才可以執(zhí)行,無(wú)法在ios系統(tǒng)上動(dòng)態(tài)更新 |
Flutter編譯模式有:Debug,Release,Profile;
Flutter編譯模式 | 特點(diǎn) |
---|---|
Debug | 對(duì)應(yīng)JIT模式,支持設(shè)備和模擬器; 打開了斷言,支持快速開發(fā),支持HotReload; 并未對(duì)包大小,執(zhí)行速度做優(yōu)化; |
Release | 對(duì)應(yīng)AOT模式,支持真機(jī),不支持模擬器; 禁止了所有斷言調(diào)試信息; 對(duì)包大小,啟動(dòng)和執(zhí)行速度進(jìn)行了優(yōu)化; |
Profile | 類似Release模式,保留了一些調(diào)試功能,幫助性能分析; |
Flutter下的iOS/Android工程本質(zhì)上是一個(gè)標(biāo)準(zhǔn)的iOS/Android的工程;IOS平臺(tái):Flutter通過(guò)在BuildPhase中添加shell(xcode_backend.sh)來(lái)生成和嵌入App.framework和Flutter.framework到ios; Android平臺(tái):Flutter通過(guò)gradle來(lái)添加flutter.jar和編譯完的二進(jìn)制文件添加到Android;
3.2.1 引擎層結(jié)構(gòu)分析:
3.2.2 Android編譯產(chǎn)物的分析
3.2.3 IOS編譯產(chǎn)物的分析
四,熱更新技術(shù)方案分析4.1 業(yè)務(wù)代碼分析根據(jù)“3.3.1” ~“3.3.2”的分析可以確定無(wú)論是IOS還是Android APP業(yè)務(wù)代碼都是由四個(gè)段組成:kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData、kDartIsolateSnapshotInstructions;理論上只要能動(dòng)態(tài)替換加載的代碼段&數(shù)據(jù)段代碼即可實(shí)現(xiàn)目標(biāo)。
名稱 | 注釋 | 作用 | 注釋 |
---|---|---|---|
kDartIsolateSnapshotData | Dart isolate數(shù)據(jù)段 | 類信息,全局變量,函數(shù)指針等 | 允許動(dòng)態(tài)下發(fā) |
kDartIsolateSnapshotInstructions | Dart isolate指令段 | 包含由Dart isolate執(zhí)行的AOT代碼 | IOS不允許動(dòng)態(tài)下發(fā) |
kDartVmSnapshotData | vm isolate數(shù)據(jù)段 | isolate 之間共享的 Dart 堆 (heap) 的初始狀態(tài) | 允許動(dòng)態(tài)下發(fā) |
kDartVmSnapshotInstructions | vm isolate指令段 | 包含 VM 中所有 Dart isolate 之間共享的通用程序的 AOT 指令 | IOS不允許動(dòng)態(tài)下發(fā) |
注釋: isolate, snapshot, vm isolate含義解釋如下:
名稱 | 含義 |
---|---|
isolate | Dart是單線程,isolate跟線程差不多,可以理解為 Dart 中的線程。 isolate 與線程的區(qū)別:線程與線程之間是共享內(nèi)存的,而 isolate 和 isolate 之間是內(nèi)存不共享的。 不存在鎖競(jìng)爭(zhēng)問題,兩個(gè)Isolate完全是兩條獨(dú)立的執(zhí)行線,且每個(gè)Isolate都有自己的事件循環(huán),它們之間只能通過(guò)發(fā)送消息通信,所以它的資源開銷低于線程。 |
snapshot | 將類信息、全局變量、函數(shù)指令直接以序列化的方式存在磁盤中,稱為 Snapshot(快照)。 |
vm isolate | 同一個(gè)進(jìn)程里可以有很多isolate,但兩個(gè) isolate 的堆區(qū)是不能共享的,所以官方設(shè)計(jì)了 VM isolate,也就是 kDartVmSnapshot,用來(lái)多個(gè) isolate 之間的交互。 |
按照4.1的分析思路,我們首先需要了解Flutter運(yùn)行時(shí)代碼加載的完整流程,經(jīng)過(guò)梳理分析流程如下:
1 )Android- APP業(yè)務(wù)代碼的加載流程:
2)IOS- APP業(yè)務(wù)代碼的加載流程:
4.3 業(yè)務(wù)代碼的編譯生成(編譯時(shí))根據(jù)以上的分析,我們知道了Flutter業(yè)務(wù)代碼的數(shù)據(jù)結(jié)構(gòu),也知道了在運(yùn)行時(shí)如何加載,因此我們只需要在編譯時(shí)做更改,產(chǎn)生自己需要的代碼段,和數(shù)據(jù)段文件。在運(yùn)行時(shí)加載自己的構(gòu)建產(chǎn)物即可達(dá)到目標(biāo)。
1)在此以 IOS 構(gòu)建自己的業(yè)務(wù)代碼流程做詳細(xì)分析:
**有完成構(gòu)建流程可以分析,基本流程是“Dart Code(業(yè)務(wù)代碼)” -> (通過(guò)Dart編譯器gen_snapshot.cc) 生成 snapshot_assemble.S 的匯編文件 -> (通過(guò)xcrun工具)生成 snapshot_assemble.o的obj文件 -> (通過(guò)xcun clang工具鏈) 生成了 App.Framework。
2)Android的產(chǎn)物構(gòu)建流程和IOS類似。由于Android有其他更簡(jiǎn)單的方案, 因此省略詳細(xì)的構(gòu)建流程分析,大致如下:
4.4 實(shí)現(xiàn)熱更新的方案探索根據(jù)上面的技術(shù)分析結(jié)果,已經(jīng)可以獨(dú)立生成自己的代碼段,數(shù)據(jù)段文件。通過(guò)需改虛擬機(jī)底層代碼的方式,也可以動(dòng)態(tài)的加載運(yùn)行。但由于IOS系統(tǒng)目前底層的系統(tǒng)還不能動(dòng)態(tài)加載可讀寫的代碼段數(shù)據(jù)到內(nèi)存中,所以還有技術(shù)難點(diǎn)需要突破。但Android端有更簡(jiǎn)單的路徑可以解決,因此下面以Android端為例重點(diǎn)分析思路,大致如下圖所示:
由上圖可以得知,Android端 熱修復(fù)核心步驟如下:
1, 修改Flutter Engine代碼,加載指定路徑的libapp.so和flutter_aasets,比如私有目錄(data/data/files);
2, 編譯APK時(shí),利用Gradle Transform插件,根據(jù)Flutter SDK的engine version動(dòng)態(tài)替換官方的Flutter engine,最終寫入修改后的engine到APK;
3, 生成補(bǔ)丁包:利用BSdiff算法比較新舊APK文件,生成patch補(bǔ)丁包
4, APP啟動(dòng)時(shí)訪問后端接口,根據(jù)參數(shù)(app的版本號(hào),補(bǔ)丁包版本號(hào),md5,flutter SDK版本號(hào),Engine版本號(hào))拉取補(bǔ)丁包;
5, 合成補(bǔ)丁包:校驗(yàn)md5,app版本號(hào),補(bǔ)丁版本號(hào),安裝時(shí)間;
6, 自定義Flutter Engine加載指定路徑的libapp.so和flutter_assets資源文件;
作者:京東科技 劉振中、周智
內(nèi)容來(lái)源:京東云開發(fā)者社區(qū)
關(guān)鍵詞: