Flutter 2.2.X 版本熱修復(fù)實(shí)現(xiàn)思路分享(Android+Flutter混開項(xiàng)目)

已經(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)

    1. libflutter.so:Flutter 引擎相關(guān)代碼,不需要關(guān)心。
    2. libapp.so:Flutter Module 會(huì)編譯成該 動(dòng)態(tài)庫(kù),我們編寫的 dart 代碼都在這里,熱修復(fù) Flutter 代碼通過替換該動(dòng)態(tài)庫(kù)實(shí)現(xiàn)。
    3. assets/flutter/xxx:Flutter Module 的資源位于 apk 包的 assets 下。
  • 熱修復(fù)窗口:貼代碼可能要貼很多代碼,我這里簡(jiǎn)單描述下該該窗口的發(fā)生時(shí)機(jī)

    1. FlutterActivity 中的委托類 FlutterActivityDelegateonCreate 函數(shù)中,會(huì)執(zhí)行 Flutter 引擎的初始化方法。

    2. 初始化方法會(huì)通過下述調(diào)用代碼:

     String[] args = getArgsFromIntent(activity.getIntent());
     FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);
    

    我們關(guān)注重點(diǎn)在 FlutterMain.ensureInitializationComplete() 中。

    1. 具體實(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ù))。
    2. 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)行尋址。
    3. 我們只需要使用 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)步驟

如下

  1. 引入 Tinker 熱修復(fù)能力,這個(gè)沒啥好說(shuō)的吧

  2. 對(duì) Flutter 資源和代碼進(jìn)行修改,方便驗(yàn)證。

  3. 開發(fā) Gradle 插件,使用 Transform api + ASM ,對(duì) FlutterJNI.init(xxx) 的函數(shù)字節(jié)碼進(jìn)行編輯。

  4. FlutterJNI.init(xxx) 的函數(shù)字節(jié)碼編輯的主要做以下操作

    1. 判斷當(dāng)前應(yīng)用是否有做 Tinker 熱修復(fù),該次熱修復(fù)操作是否有下發(fā) libapp.so 動(dòng)態(tài)庫(kù)。

    2. 獲取 libapp.so 動(dòng)態(tài)庫(kù)的絕對(duì)地址,修改字符串?dāng)?shù)組中 --aot-shared-library-name= 的字符串元素的值指向該絕對(duì)地址。

    3. 實(shí)現(xiàn) Flutter 代碼熱修復(fù),而 Flutter 資源熱修復(fù)不需關(guān)心,Tinker 本身支持 assets 的熱修復(fù),不需要額外處理。

    4. 該步驟還可以優(yōu)化,插件的開啟時(shí)機(jī)建議在只做 Flutter 熱修復(fù)的時(shí)候才開啟。

    5. 使用 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)贊支持下,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容