已經(jīng)好久沒有寫文章了,最近被業(yè)務(wù)折磨的一塌糊涂,跟個(gè)陀螺似的,被逆時(shí)針、順時(shí)針來(lái)回地抽/(ㄒoㄒ)/。
好久前就想捋一捋 Flutter 混開項(xiàng)目中熱修復(fù)的實(shí)現(xiàn)方式,最近在業(yè)余時(shí)間耗費(fèi)了一些精力終于實(shí)現(xiàn)了該功能,故在此分享下實(shí)現(xiàn)思路。
cry.jfif
當(dāng)前文章中的 FlutterSDK 特指 2.2.2 版本,新版本 SDK 中涉及熱修復(fù)窗口的相關(guān)代碼發(fā)現(xiàn)了變化,1.X 版本的實(shí)現(xiàn)方式已經(jīng)過時(shí)了。
實(shí)現(xiàn)原理
我的實(shí)現(xiàn)思路用到 Tinker + ASM + 自定義GradlePlugin ,Tinker 用于打資源差分包,ASM 用于編輯關(guān)鍵類的字節(jié)碼,GradlePlugin 中使用 Transform 在 Android 項(xiàng)目編譯期間去使用 ASM 的字節(jié)碼編輯功能。
在 Flutter 的早期版本中,Google 是有提供官方的熱修復(fù)能力的,但是在后期的迭代版本中又將該能力給屏蔽掉了。但是其仍然留有一個(gè)窗口給到我們?nèi)?duì) Flutter 代碼與資源進(jìn)行熱修復(fù)(特指Android+Flutter混開項(xiàng)目),只要了解了該窗口的具體發(fā)生時(shí)機(jī) 與 Flutter Module 構(gòu)建產(chǎn)物 的知識(shí),我們便可以很直觀地實(shí)現(xiàn) Flutter 的熱修復(fù)功能。
-
Flutter 構(gòu)建產(chǎn)物(release):我們只需要關(guān)注第2與3點(diǎn)
- libflutter.so:Flutter 引擎相關(guān)代碼,不需要關(guān)心。
- libapp.so:Flutter Module 會(huì)編譯成該 動(dòng)態(tài)庫(kù),我們編寫的 dart 代碼都在這里,熱修復(fù) Flutter 代碼通過替換該動(dòng)態(tài)庫(kù)實(shí)現(xiàn)。
- assets/flutter/xxx:Flutter Module 的資源位于 apk 包的 assets 下。
-
熱修復(fù)窗口:貼代碼可能要貼很多代碼,我這里簡(jiǎn)單描述下該該窗口的發(fā)生時(shí)機(jī)
FlutterActivity中的委托類FlutterActivityDelegate的onCreate函數(shù)中,會(huì)執(zhí)行 Flutter 引擎的初始化方法。初始化方法會(huì)通過下述調(diào)用代碼:
String[] args = getArgsFromIntent(activity.getIntent()); FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);我們關(guān)注重點(diǎn)在
FlutterMain.ensureInitializationComplete()中。- 具體實(shí)現(xiàn)代碼就不展開,
FlutterMain.ensureInitializationComplete()中會(huì)有libapp.so動(dòng)態(tài)庫(kù)的位置參數(shù)的配置與傳遞,它最終的是通過FlutterJNI.init(xxx)函數(shù)進(jìn)行初始化(注意:該libapp.so動(dòng)態(tài)庫(kù)的加載與平時(shí)的loadLibrary(String libname)機(jī)制不同,不能簡(jiǎn)單的使用 Tinker 進(jìn)行熱修復(fù))。 -
FlutterJNI.init(xxx)函數(shù)的入?yún)?String[] args會(huì)包含2個(gè)帶有--aot-shared-library-name=的字符串元素用于指定libapp.so動(dòng)態(tài)庫(kù)的位置,其中一個(gè)是相對(duì)位置,一個(gè)是絕對(duì)位置。相對(duì)位置尋址失敗便會(huì)使用絕對(duì)位置進(jìn)行尋址。 - 我們只需要使用 ASM + GradlePlugin 編輯
FlutterJNI.init(xxx)函數(shù)的字節(jié)碼,將--aot-shared-library-name=字符串元素指定為我們自定義的動(dòng)態(tài)庫(kù)絕對(duì)路徑即可實(shí)現(xiàn) Flutter 代碼的熱修復(fù)。
實(shí)現(xiàn)步驟
如下
引入 Tinker 熱修復(fù)能力,這個(gè)沒啥好說(shuō)的吧
對(duì) Flutter 資源和代碼進(jìn)行修改,方便驗(yàn)證。
開發(fā) Gradle 插件,使用 Transform api + ASM ,對(duì)
FlutterJNI.init(xxx)的函數(shù)字節(jié)碼進(jìn)行編輯。-
FlutterJNI.init(xxx)的函數(shù)字節(jié)碼編輯的主要做以下操作判斷當(dāng)前應(yīng)用是否有做 Tinker 熱修復(fù),該次熱修復(fù)操作是否有下發(fā)
libapp.so動(dòng)態(tài)庫(kù)。獲取
libapp.so動(dòng)態(tài)庫(kù)的絕對(duì)地址,修改字符串?dāng)?shù)組中--aot-shared-library-name=的字符串元素的值指向該絕對(duì)地址。實(shí)現(xiàn) Flutter 代碼熱修復(fù),而 Flutter 資源熱修復(fù)不需關(guān)心,Tinker 本身支持 assets 的熱修復(fù),不需要額外處理。
該步驟還可以優(yōu)化,插件的開啟時(shí)機(jī)建議在只做 Flutter 熱修復(fù)的時(shí)候才開啟。
使用 Gradle 插件,執(zhí)行 Tinker patch 生成的 Task,安裝補(bǔ)丁重啟驗(yàn)證。
按照以上步驟,本人親測(cè)可實(shí)現(xiàn) Flutter 的熱修復(fù)功能(Android+Flutter混開項(xiàng)目)。
小結(jié)
因精力有限,文章中很多內(nèi)容沒有展開描述,如有錯(cuò)誤還望指出,共同進(jìn)步哈。
如果你覺得本篇文章有用的話還請(qǐng)點(diǎn)贊支持下,謝謝!