快速接入美團(tuán)熱修復(fù)Robust

相信很多人都認(rèn)識了解過 熱修復(fù)、熱更新、熱補(bǔ)丁(對于這個技術(shù)也沒有特別標(biāo)準(zhǔn)的一種叫法,下面我統(tǒng)一叫熱更新),之后的一年里,各種熱更新方案如雨后春筍般出現(xiàn),比較耳熟能詳?shù)木陀蠳uwa、Tinker、Andfix 和 Dexposed 等等,他們之間的區(qū)別以及優(yōu)缺點(diǎn)就不在這里討論了,鑒于它們的實(shí)際使用和局限性,美團(tuán)的開發(fā)團(tuán)隊(duì)就腦洞大開了。


image.png

去年 Google 高調(diào)發(fā)布了 Android Studio 2.0,其中最重要的新特性Instant Run,實(shí)現(xiàn)了對代碼修改的實(shí)時生效(熱插拔)。美團(tuán)開發(fā)團(tuán)隊(duì)在了解 Instant Run 原理之后,實(shí)現(xiàn)了一個兼容性更強(qiáng)的熱更新方案,這就是產(chǎn)品化的hotpatch框架—–Robust,對于 Robust 的原理我們后面的文章再討論,本篇只針對想快速上手的入門講解

Android熱更新方案Robust

Android熱更新方案Robust開源,新增自動化補(bǔ)丁工具

美團(tuán) Robust 的 github demo 地址

image.png

Robust 為每個類新增了一個類型為 ChangeQuickRedirect 的靜態(tài)變量,并且在每個方法前,增加判斷該變量是否為空的邏輯,如果不為空,走打基礎(chǔ)包時插樁的邏輯,否則走正常邏輯。
使用步驟

1.集成了 Robust 后,生成 apk。保存期間的混淆文件 mapping.txt,以及 Robust 生成記錄文件 methodMap.robust 
2.使用注解 @Modify 或者方法 RobustModify.modify() 標(biāo)注需要修復(fù)的方法 
3.開啟補(bǔ)丁插件,執(zhí)行生成 apk 命令,獲得補(bǔ)丁包 patch.jar 
4.通過推送或者接口的形式,通知 app 有補(bǔ)丁,需要修復(fù) 
5.加載補(bǔ)丁文件不需要重新啟動應(yīng)用

集成方法
1、在項(xiàng)目最外層的 build.gradle 添加兩處插件

  classpath 'com.meituan.robust:gradle-plugin:0.3.3'
  classpath 'com.meituan.robust:auto-patch-plugin:0.3.3'

2、然后在項(xiàng)目的 build.gradle 添加

//apply plugin: 'auto-patch-plugin'(生成補(bǔ)丁的時候打開)
 apply plugin: 'robust'(生成apk的時候打開)
 compile 'com.meituan.robust:robust:0.3.3'

3、需要手動 copy 一份 robust.xml 的配置文件到app的目錄下,該文件各個配置注釋的很清楚,若沒特殊要求,不需要修改

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <switch>
        <!--true代表打開Robust,請注意即使這個值為true,Robust也默認(rèn)只在Release模式下開啟-->
        <!--false代表關(guān)閉Robust,無論是Debug還是Release模式都不會運(yùn)行robust-->
        <turnOnRobust>true</turnOnRobust>
        <!--<turnOnRobust>false</turnOnRobust>-->

        <!--是否開啟手動模式,手動模式會去尋找配置項(xiàng)patchPackname包名下的所有類,自動的處理混淆,然后把patchPackname包名下的所有類制作成補(bǔ)丁-->
        <!--這個開關(guān)只是把配置項(xiàng)patchPackname包名下的所有類制作成補(bǔ)丁,適用于特殊情況,一般不會遇到-->
        <!--<manual>true</manual>-->
        <manual>false</manual>

        <!--是否強(qiáng)制插入插入代碼,Robust默認(rèn)在debug模式下是關(guān)閉的,開啟這個選項(xiàng)為true會在debug下插入代碼-->
        <!--但是當(dāng)配置項(xiàng)turnOnRobust是false時,這個配置項(xiàng)不會生效-->
        <!--<forceInsert>true</forceInsert>-->
        <forceInsert>false</forceInsert>

        <!--是否捕獲補(bǔ)丁中所有異常,建議上線的時候這個開關(guān)的值為true,測試的時候?yàn)閒alse-->
        <catchReflectException>true</catchReflectException>
        <!--<catchReflectException>false</catchReflectException>-->

        <!--是否在補(bǔ)丁加上log,建議上線的時候這個開關(guān)的值為false,測試的時候?yàn)閠rue-->
        <!--<patchLog>true</patchLog>-->
        <patchLog>false</patchLog>

        <!--項(xiàng)目是否支持progaurd-->
        <proguard>true</proguard>
        <!--<proguard>false</proguard>-->

        <!--項(xiàng)目是否支持ASM進(jìn)行插樁,默認(rèn)使用ASM,推薦使用ASM,Javaassist在容易和其他字節(jié)碼工具相互干擾-->
        <useAsm>true</useAsm>
        <!--<useAsm>false</useAsm>-->
    </switch>

    <!--需要熱補(bǔ)的包名或者類名,這些包名下的所有類都被會插入代碼-->
    <!--這個配置項(xiàng)是各個APP需要自行配置,就是你們App里面你們自己代碼的包名,
    這些包名下的類會被Robust插入代碼,沒有被Robust插入代碼的類Robust是無法修復(fù)的-->
    <packname name="hotfixPackage">
        <name>com.project</name>
    </packname>

    <!--不需要Robust插入代碼的包名,Robust庫不需要插入代碼,如下的配置項(xiàng)請保留,還可以根據(jù)各個APP的情況執(zhí)行添加-->
    <exceptPackname name="exceptPackage">

    </exceptPackname>

    <!--補(bǔ)丁的包名,請保持和類PatchManipulateImp中fetchPatchList方法中設(shè)置的補(bǔ)丁類名保持一致( setPatchesInfoImplClassFullName("com.meituan.robust.patch.PatchesInfoImpl")),
    各個App可以獨(dú)立定制,需要確保的是setPatchesInfoImplClassFullName設(shè)置的包名是如下的配置項(xiàng),類名必須是:PatchesInfoImpl-->
    <patchPackname name="patchPackname">
        <name>com.project</name>
    </patchPackname>

    <!--自動化補(bǔ)丁中,不需要反射處理的類,這個配置項(xiàng)慎重選擇-->
    <noNeedReflectClass name="classes no need to reflect">

    </noNeedReflectClass>
</resources>

現(xiàn)在可以編譯項(xiàng)目 時間會比較長


build.png

先解釋一下,在生成 apk 的時候使用 apply plugin:'robust',該插件會生成打補(bǔ)丁時需要的方法記錄文件 methodMap.robust,該文件在打補(bǔ)丁的時候用來區(qū)別到底哪些方法需要被修復(fù),所以有它才能打補(bǔ)丁。而上文所說的還有 mapping.txt 文件,該文件列出了原始的類,方法和字段名與混淆后代碼間的映射。這個文件很重要,可以用它來翻譯被混淆的代碼。但也不是必須的,如果不需要混淆,可以不保留。這兩個文件在生成apk后,分別在 build/outputs/robust/methodsMap.robust,build/outputs/mapping/mapping.txt(需要開啟混淆后才會出現(xiàn)),我們需要自己分別拷貝到 app/robust 下,在 app 目錄新建個叫 robust 的文件夾,把這兩個文件放進(jìn)去就 ok 了。


WX20180307-113912@2x.png

完成了第二步了。我們得到了 apk ,mapping.txt,methodMap.robust ,有了它們我們再繼續(xù)生成補(bǔ)丁 patch.jar。

加載補(bǔ)丁的方法

  new PatchExecutor(getActivity(), new PatchManipulateImp(), new RobustCallBack() {
                    @Override
                    public void onPatchListFetched(boolean b, boolean b1) {

                    }

                    @Override
                    public void onPatchFetched(boolean b, boolean b1, Patch patch) {

                    }

                    @Override
                    public void onPatchApplied(boolean b, Patch patch) {

                    }

                    @Override
                    public void logNotify(String s, String s1) {

                    }

                    @Override
                    public void exceptionNotify(Throwable throwable, String s) {

                    }
                }).start();

注意 PatchManipulateImp
關(guān)于PatchManipulateImp這個類控制了補(bǔ)丁的加載策略,代碼中也增加不少的注釋,在這個類中實(shí)際負(fù)責(zé)補(bǔ)丁的下載、校驗(yàn)和使用策略等,這個類需要實(shí)現(xiàn)如下幾個方法

要加載補(bǔ)丁肯定得知道 patch.jar 放在哪啊是吧,打開看一眼(不要害怕只有很少代碼),為了方便展示,就把不太重要的三個方法縮起來了,copy 方法是普通文件拷貝的IO流,verifyPatch 方法本來是驗(yàn)證補(bǔ)丁有效性的,后來發(fā)現(xiàn)對普通使用者沒有那么高要求,就改成了備份補(bǔ)丁的回調(diào)了,ensurePatchExist 方法就檢測補(bǔ)丁存在與否,好了大概知道其它的方法就詳細(xì)介紹下主要方法 fetchPatchList,回顧一下上面的xxx再聯(lián)想到這里吧,既然我們要加載補(bǔ)丁,那么我們得知道補(bǔ)丁在哪啊,這個方法就是把補(bǔ)丁找出來給上面那個誰用的。所以說,補(bǔ)丁的位置你可以根據(jù)拉取下來保存的位置來找出來,把路徑給 setLocalPath 就好。再有就 setPatchesInfoImplClassFullName 的包名需要和 robust.xml 配置的一樣


4.png
5.png

新增加一個頁面來測試補(bǔ)丁

public class RobustActivity extends AppCompatActivity {
    private TextView text2;

    @Override
    @Modify
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_robust);
        text2 = findViewById(R.id.text2);
        // text2.setText(getString());
        text2.setText(getinfo());
    }

    private String getString() {
        return "hello word";
    }

    @Add
    public String getinfo() {
        StringBuilder msg = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            msg.append(i + "\n");
        }
        return msg.toString();
    }
}

制作補(bǔ)丁并使用

我們所需要做的跳轉(zhuǎn)后效果是修改一下textview顯示內(nèi)容而已,那么在被修改的頁面需要怎么標(biāo)注被修改的方法呢,就像這樣,先科普一下,在 robust 的注意事項(xiàng)里面已經(jīng)提到過,修改方法和字段會有一些局限性,那是因?yàn)?android 本身ProGuard的內(nèi)聯(lián)、優(yōu)化導(dǎo)致的。所以要繞過這個本身的問題,要必須遵循一些規(guī)律了...以后再介紹這個。修改完這些,我們再去 build.gradle 修改一下,開啟打補(bǔ)丁那個,關(guān)閉生成apk那個。之后再在終端執(zhí)行一遍生成apk的命令行。直到終端那里出現(xiàn) auto patch end successfully

美團(tuán)已經(jīng)提供了自動化生成補(bǔ)丁的工具
在項(xiàng)目的 build.gradle apply plugin: 'auto-patch-plugin'打開
注釋掉生成apk的插件
再次點(diǎn)擊 assembleRelease
出現(xiàn) auto patch end successfully 表示補(bǔ)丁已經(jīng)生成 此時在robust文件里面可以看到patch.jar


3.png

點(diǎn)擊load_path


image.png

如果看到 apply result true 那么就大功告成了。
再點(diǎn)擊jump
便可以看到加載的是補(bǔ)丁里面的方法

最后來張效果圖


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

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

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