已經(jīng)好久沒有寫文章了赡模,最近被業(yè)務(wù)折磨的一塌糊涂田炭,跟個(gè)陀螺似的,被逆時(shí)針漓柑、順時(shí)針來回地抽/(ㄒoㄒ)/教硫。
好久前就想捋一捋 Flutter 混開項(xiàng)目中熱修復(fù)的實(shí)現(xiàn)方式,最近在業(yè)余時(shí)間耗費(fèi)了一些精力終于實(shí)現(xiàn)了該功能辆布,故在此分享下實(shí)現(xiàn)思路瞬矩。
當(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)庫,我們編寫的 dart 代碼都在這里谬盐,熱修復(fù) Flutter 代碼通過替換該動(dòng)態(tài)庫實(shí)現(xiàn)甸私。
- assets/flutter/xxx:Flutter Module 的資源位于 apk 包的 assets 下。
-
熱修復(fù)窗口:貼代碼可能要貼很多代碼飞傀,我這里簡單描述下該該窗口的發(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)庫的位置參數(shù)的配置與傳遞,它最終的是通過FlutterJNI.init(xxx)
函數(shù)進(jìn)行初始化(注意:該libapp.so
動(dòng)態(tài)庫的加載與平時(shí)的loadLibrary(String libname)
機(jī)制不同幢痘,不能簡單的使用 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)庫的位置,其中一個(gè)是相對(duì)位置,一個(gè)是絕對(duì)位置购岗。相對(duì)位置尋址失敗便會(huì)使用絕對(duì)位置進(jìn)行尋址汰聋。 - 我們只需要使用 ASM + GradlePlugin 編輯
FlutterJNI.init(xxx)
函數(shù)的字節(jié)碼,將--aot-shared-library-name=
字符串元素指定為我們自定義的動(dòng)態(tài)庫絕對(duì)路徑即可實(shí)現(xiàn) Flutter 代碼的熱修復(fù)喊积。
實(shí)現(xiàn)步驟
如下
引入 Tinker 熱修復(fù)能力烹困,這個(gè)沒啥好說的吧
對(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)庫。獲取
libapp.so
動(dòng)態(tài)庫的絕對(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)證嫉入。
按照以上步驟,本人親測可實(shí)現(xiàn) Flutter 的熱修復(fù)功能(Android+Flutter混開項(xiàng)目)璧尸。
小結(jié)
因精力有限咒林,文章中很多內(nèi)容沒有展開描述,如有錯(cuò)誤還望指出爷光,共同進(jìn)步哈垫竞。
如果你覺得本篇文章有用的話還請(qǐng)點(diǎn)贊支持下,謝謝蛀序!