当前版本:0.4.99(目前不支持AGP4.1.0:https://github.com/Meituan-Dianping/Robust/issues/434,可以用0.4.100试试)

 

项目的build.gradle
dependencies {
classpath 'com.meituan.robust:gradle-plugin:0.4.99'
classpath 'com.meituan.robust:auto-patch-plugin:0.4.99'
}
APP的build.gradle
apply plugin: 'com.android.application'
//apply plugin: 'robust' 打基准包的时候用
apply plugin: 'auto-patch-plugin' 打补丁包的时候用

 

打基准包

拷贝robust.xml到src同级目录,需要修改的地方:<packname name="hotfixPackage"> <patchPackname name="patchPackname">,里面的值分别对应包名和包名.patch

继承com.meituan.robust.PatchManipulate,实现com.meituan.robust.RobustCallBack,代码放最后,都是从官方demo拿过来的

选一个合适的时机,调用new PatchExecutor(context, new TestPatchManipulate(), new TestRobustCallBack()).start();,加载补丁包

 

开启混淆,才能生成mapping文件

放开APP的build.gradle中基准包的引用,注释补丁包的引用

gradle --> app --> 找到assembleRelease (这一步需要配置签名信息)

执行成功后,会在build --> outputs --> 下面生成对应的文件,需要检查的有apk文件夹下的release>app-release.apk,mapping文件夹下的release>mapping.txt,robust文件夹下的methodsMap.robust

 

补丁包

拷贝上面的3个文件到src同级的robust文件夹下

修改代码,有修改的方法用@Modify注解,新增的方法用@Add,Lambda需要在方法内部调用RobustModify.modify(),参考官方文档

放开APP的build.gradle中补丁包的引用,注释基准包的引用,同步

run app,不出意外的话,Build会出错误提示:Cause: auto patch end successfully,出这个提示没问题,表示补丁包生成成功

在src同级的robust文件夹下,可以找到一个patch.jar,就是我们需要的补丁文件了

 

下发和校验

线上发布的下发和校验,可以灵活处理,主要逻辑在集成PatchManipulate的方法fetchPatchList和verifyPatch里面

理论上,robust实时生效,收到补丁包更新信息之后,下载补丁包到指定目录,调用预先设置的加载补丁包的方法,就OK了,实际运行也OK

检查方法:将patch.jar拷贝到手机根目录下的robust文件夹下,重新运行APP,需要权限
资源地址:,安装包在资源解压后src的同级robust文件夹下

new PatchExecutor(context, new TestPatchManipulate(), new TestRobustCallBack()).start();

有问题看日志,主要是在RobustCallBack中

——————————————————下面是部分代码—————————————————————
package com.wy.testrobust;
 
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.meituan.robust.Patch;
import com.meituan.robust.PatchManipulate;
 
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
/**
* @Description: robust PatchManipulate
* @Author: ***
* @Date: 2021/6/4 5:18 下午
*/
public class TestPatchManipulate extends PatchManipulate {
@Override
protected List<Patch> fetchPatchList(Context context) {
String robustApkHash = RobustApkHashUtils.readRobustApkHash(context);
Log.w("robust","robustApkHash :" + robustApkHash);
//connect to network to get patch list on servers
//在这里去联网获取补丁列表
Patch patch = new Patch();
patch.setName("123");
//we recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar
//LocalPath是存储原始的补丁文件,这个文件应该是加密过的,TempPath是加密之后的,TempPath下的补丁加载完毕就删除,保证安全性
//这里面需要设置一些补丁的信息,主要是联网的获取的补丁信息。重要的如MD5,进行原始补丁文件的简单校验,以及补丁存储的位置,这边推荐把补丁的储存位置放置到应用的私有目录下,保证安全性
patch.setLocalPath(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "robust" + File.separator + "patch");
 
//setPatchesInfoImplClassFullName 设置项各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是和xml配置项patchPackname保持一致,而且类名必须是:PatchesInfoImpl
//请注意这里的设置
patch.setPatchesInfoImplClassFullName("com.wy.testrobust.patch.PatchesInfoImpl");
List patches = new ArrayList<Patch>();
patches.add(patch);
return patches;
}
 
@Override
protected boolean verifyPatch(Context context, Patch patch) {
patch.setTempPath(context.getCacheDir() + File.separator + "robust" + File.separator + "patch");
try {
copy(patch.getLocalPath(), patch.getTempPath());
} catch (Exception e) {
String msg = Log.getStackTraceString(e);
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
});
}
return true;
}
 
@Override
protected boolean ensurePatchExist(Patch patch) {
return true;
}
 
 
public void copy(String srcPath, String dstPath) throws IOException {
File src = new File(srcPath);
if (!src.exists()) {
throw new RuntimeException("source patch does not exist ");
}
File dst = new File(dstPath);
if (!dst.getParentFile().exists()) {
dst.getParentFile().mkdirs();
}
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
out.close();
}
} finally {
in.close();
}
}
}
——————————————————
package com.wy.testrobust;
 
import android.content.Context;
import android.text.TextUtils;
import com.meituan.robust.Constants;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
/**
* Created by hedex on 17/2/27.
*/
public class RobustApkHashUtils {
private static String robustApkHashValue;
 
public static String readRobustApkHash(Context context) {
 
if (TextUtils.isEmpty(robustApkHashValue)) {
robustApkHashValue = readRobustApkHashFile(context);
}
 
return robustApkHashValue;
}
 
private static String readRobustApkHashFile(Context context) {
String value = "";
if (null == context) {
return value;
}
 
try {
value = readFirstLine(context, Constants.ROBUST_APK_HASH_FILE_NAME);
} catch (Throwable throwable) {
 
}
 
return value;
}
 
private static String readFirstLine(Context context, String fileName) {
BufferedReader reader = null;
try {
reader = new BufferedReader(
new InputStreamReader(context.getAssets().open(fileName)));
 
return reader.readLine();
} catch (IOException e) {
return "";
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
}
}
}
}
 
}
————————————————————
package com.wy.testrobust;
 
import android.util.Log;
import com.meituan.robust.Patch;
import com.meituan.robust.RobustCallBack;
 
import java.util.List;
 
/**
* @Description: robust TestRobustCallBack
* @Author: *****
* @Date: 2021/6/4 5:19 下午
*/
public class TestRobustCallBack implements RobustCallBack {
@Override
public void onPatchListFetched(boolean result, boolean isNet, List<Patch> patches) {
Log.e("RobustCallBack", "onPatchListFetched result: " + result);
Log.e("RobustCallBack", "onPatchListFetched isNet: " + isNet);
for (Patch patch : patches) {
Log.e("RobustCallBack", "onPatchListFetched patch: " + patch.getName());
}
}
 
@Override
public void onPatchFetched(boolean result, boolean isNet, Patch patch) {
Log.e("RobustCallBack", "onPatchFetched result: " + result);
Log.e("RobustCallBack", "onPatchFetched isNet: " + isNet);
Log.e("RobustCallBack", "onPatchFetched patch: " + patch.getName());
}
 
@Override
public void onPatchApplied(boolean result, Patch patch) {
Log.e("RobustCallBack", "onPatchApplied result: " + result);
Log.e("RobustCallBack", "onPatchApplied patch: " + patch.getName());
}
 
@Override
public void logNotify(String log, String where) {
Log.e("RobustCallBack", "logNotify log: " + log);
Log.e("RobustCallBack", "logNotify where: " + where);
}
 
@Override
public void exceptionNotify(Throwable throwable, String where) {
Log.e("RobustCallBack", "exceptionNotify where: " + where, throwable);
}
}