相信很多人都認識了解過 熱修復省古、熱更新粥庄、熱補丁(對于這個技術(shù)也沒有特別標準的一種叫法,下面我統(tǒng)一叫熱更新)豺妓,之后的一年里惜互,各種熱更新方案如雨后春筍般出現(xiàn)布讹,比較耳熟能詳?shù)木陀蠳uwa、Tinker训堆、Andfix 和 Dexposed 等等描验,他們之間的區(qū)別以及優(yōu)缺點就不在這里討論了,鑒于它們的實際使用和局限性坑鱼,美團的開發(fā)團隊就腦洞大開了膘流。
去年 Google 高調(diào)發(fā)布了 Android Studio 2.0,其中最重要的新特性Instant Run姑躲,實現(xiàn)了對代碼修改的實時生效(熱插拔)睡扬。美團開發(fā)團隊在了解 Instant Run 原理之后,實現(xiàn)了一個兼容性更強的熱更新方案黍析,這就是產(chǎn)品化的hotpatch框架—–Robust,對于 Robust 的原理我們后面的文章再討論屎开,本篇只針對想快速上手的入門講解
Android熱更新方案Robust開源阐枣,新增自動化補丁工具
Robust 為每個類新增了一個類型為 ChangeQuickRedirect 的靜態(tài)變量,并且在每個方法前奄抽,增加判斷該變量是否為空的邏輯蔼两,如果不為空,走打基礎包時插樁的邏輯逞度,否則走正常邏輯额划。
使用步驟
1.集成了 Robust 后,生成 apk档泽。保存期間的混淆文件 mapping.txt俊戳,以及 Robust 生成記錄文件 methodMap.robust
2.使用注解 @Modify 或者方法 RobustModify.modify() 標注需要修復的方法
3.開啟補丁插件,執(zhí)行生成 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在容易和其他字節(jié)碼工具相互干擾-->
<useAsm>true</useAsm>
<!--<useAsm>false</useAsm>-->
</switch>
<!--需要熱補的包名或者類名,這些包名下的所有類都被會插入代碼-->
<!--這個配置項是各個APP需要自行配置鲫咽,就是你們App里面你們自己代碼的包名签赃,
這些包名下的類會被Robust插入代碼,沒有被Robust插入代碼的類Robust是無法修復的-->
<packname name="hotfixPackage">
<name>com.project</name>
</packname>
<!--不需要Robust插入代碼的包名分尸,Robust庫不需要插入代碼锦聊,如下的配置項請保留,還可以根據(jù)各個APP的情況執(zhí)行添加-->
<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>
現(xiàn)在可以編譯項目 時間會比較長
先解釋一下仰税,在生成 apk 的時候使用 apply plugin:'robust'构资,該插件會生成打補丁時需要的方法記錄文件 methodMap.robust,該文件在打補丁的時候用來區(qū)別到底哪些方法需要被修復陨簇,所以有它才能打補丁吐绵。而上文所說的還有 mapping.txt 文件,該文件列出了原始的類河绽,方法和字段名與混淆后代碼間的映射己单。這個文件很重要,可以用它來翻譯被混淆的代碼耙饰。但也不是必須的纹笼,如果不需要混淆,可以不保留苟跪。這兩個文件在生成apk后廷痘,分別在 build/outputs/robust/methodsMap.robust蔓涧,build/outputs/mapping/mapping.txt(需要開啟混淆后才會出現(xiàn)),我們需要自己分別拷貝到 app/robust 下笋额,在 app 目錄新建個叫 robust 的文件夾元暴,把這兩個文件放進去就 ok 了。
完成了第二步了兄猩。我們得到了 apk 茉盏,mapping.txt,methodMap.robust 枢冤,有了它們我們再繼續(xù)生成補丁 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這個類控制了補丁的加載策略,代碼中也增加不少的注釋淹真,在這個類中實際負責補丁的下載讶迁、校驗和使用策略等,這個類需要實現(xiàn)如下幾個方法
要加載補丁肯定得知道 patch.jar 放在哪啊是吧趟咆,打開看一眼(不要害怕只有很少代碼)添瓷,為了方便展示,就把不太重要的三個方法縮起來了值纱,copy 方法是普通文件拷貝的IO流,verifyPatch 方法本來是驗證補丁有效性的坯汤,后來發(fā)現(xiàn)對普通使用者沒有那么高要求虐唠,就改成了備份補丁的回調(diào)了,ensurePatchExist 方法就檢測補丁存在與否惰聂,好了大概知道其它的方法就詳細介紹下主要方法 fetchPatchList疆偿,回顧一下上面的xxx再聯(lián)想到這里吧,既然我們要加載補丁搓幌,那么我們得知道補丁在哪啊杆故,這個方法就是把補丁找出來給上面那個誰用的。所以說溉愁,補丁的位置你可以根據(jù)拉取下來保存的位置來找出來处铛,把路徑給 setLocalPath 就好。再有就 setPatchesInfoImplClassFullName 的包名需要和 robust.xml 配置的一樣
新增加一個頁面來測試補丁
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();
}
}
制作補丁并使用
我們所需要做的跳轉(zhuǎn)后效果是修改一下textview顯示內(nèi)容而已拐揭,那么在被修改的頁面需要怎么標注被修改的方法呢撤蟆,就像這樣,先科普一下堂污,在 robust 的注意事項里面已經(jīng)提到過家肯,修改方法和字段會有一些局限性,那是因為 android 本身ProGuard的內(nèi)聯(lián)盟猖、優(yōu)化導致的讨衣。所以要繞過這個本身的問題换棚,要必須遵循一些規(guī)律了...以后再介紹這個。修改完這些反镇,我們再去 build.gradle 修改一下固蚤,開啟打補丁那個,關閉生成apk那個愿险。之后再在終端執(zhí)行一遍生成apk的命令行颇蜡。直到終端那里出現(xiàn) auto patch end successfully
美團已經(jīng)提供了自動化生成補丁的工具
在項目的 build.gradle apply plugin: 'auto-patch-plugin'打開
注釋掉生成apk的插件
再次點擊 assembleRelease
出現(xiàn) auto patch end successfully 表示補丁已經(jīng)生成 此時在robust文件里面可以看到patch.jar
點擊load_path
如果看到 apply result true 那么就大功告成了。
再點擊jump
便可以看到加載的是補丁里面的方法
最后來張效果圖