快速接入美團熱修復Robust

相信很多人都認識了解過 熱修復、熱更新、熱補丁(對于這個技術也沒有特別標準的一種叫法,下面我統一叫熱更新),之后的一年里,各種熱更新方案如雨后春筍般出現,比較耳熟能詳的就有Nuwa、Tinker、Andfix 和 Dexposed 等等,他們之間的區別以及優缺點就不在這里討論了,鑒于它們的實際使用和局限性,美團的開發團隊就腦洞大開了。


image.png

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

Android熱更新方案Robust

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

美團 Robust 的 github demo 地址

image.png

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

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

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

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

2、然后在項目的 build.gradle 添加

//apply plugin: 'auto-patch-plugin'(生成補丁的時候打開)
 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也默認只在Release模式下開啟-->
        <!--false代表關閉Robust,無論是Debug還是Release模式都不會運行robust-->
        <turnOnRobust>true</turnOnRobust>
        <!--<turnOnRobust>false</turnOnRobust>-->

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

        <!--是否強制插入插入代碼,Robust默認在debug模式下是關閉的,開啟這個選項為true會在debug下插入代碼-->
        <!--但是當配置項turnOnRobust是false時,這個配置項不會生效-->
        <!--<forceInsert>true</forceInsert>-->
        <forceInsert>false</forceInsert>

        <!--是否捕獲補丁中所有異常,建議上線的時候這個開關的值為true,測試的時候為false-->
        <catchReflectException>true</catchReflectException>
        <!--<catchReflectException>false</catchReflectException>-->

        <!--是否在補丁加上log,建議上線的時候這個開關的值為false,測試的時候為true-->
        <!--<patchLog>true</patchLog>-->
        <patchLog>false</patchLog>

        <!--項目是否支持progaurd-->
        <proguard>true</proguard>
        <!--<proguard>false</proguard>-->

        <!--項目是否支持ASM進行插樁,默認使用ASM,推薦使用ASM,Javaassist在容易和其他字節碼工具相互干擾-->
        <useAsm>true</useAsm>
        <!--<useAsm>false</useAsm>-->
    </switch>

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

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

    </exceptPackname>

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

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

    </noNeedReflectClass>
</resources>

現在可以編譯項目 時間會比較長


build.png

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


WX20180307-113912@2x.png

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

加載補丁的方法

  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
關于PatchManipulateImp這個類控制了補丁的加載策略,代碼中也增加不少的注釋,在這個類中實際負責補丁的下載、校驗和使用策略等,這個類需要實現如下幾個方法

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


4.png
5.png

新增加一個頁面來測試補丁

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();
    }
}

制作補丁并使用

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

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


3.png

點擊load_path


image.png

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

最后來張效果圖


image.png
image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容