android 世面上的热更新方案有很多,例如QQ超级补丁 QZone 微信补丁方案 TinKer 阿里的AndFix 和美团的Robust 具体他们的优缺点网上一大堆 感兴趣的可以去深入了解一下 在这里就不一一说了,这里主要说的是美团的Robust的集成步骤
1.在项目最外层的 build.gradle 添加两处插件
classpath 'com.meituan.robust:gradle-plugin:0.4.91'
classpath 'com.meituan.robust:auto-patch-plugin:0.4.91
2.在项目App下面的 build.gradle 添加如下依赖
//制作补丁时将这个打开,auto-patch-plugin紧跟着com.android.application
// apply plugin: 'auto-patch-plugin'
apply plugin: 'robust'
dependencies {
implementation 'com.meituan.robust:robust:0.4.91'
}
3.在App文件夹下创建 robust.xml 文件 内容可以copy 下面的内容 具体的设置都有很详细的注释
true
false
false
true
true
false
true
com.meituan
com.sankuai
com.dianping
com.lysoft.robustdemo
com.meituan.robust
com.meituan.sample.extension
com.lysoft.robustdemo
接下来你要先把如何加载补丁的逻辑 写出来 我这里是点击按钮去加载手机SD卡里面我提前放好的补丁jar包
new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new RobustCallBackSample()).start();
public class PatchManipulateImp extends PatchManipulate {
/***
* connect to the network ,get the latest patches
* l联网获取最新的补丁
* @param context
*
* @return
*/
@Override
protected List fetchPatchList(Context context) {
//将app自己的robustApkHash上报给服务端,服务端根据robustApkHash来区分每一次apk build来给app下发补丁
//apkhash is the unique identifier for apk,so you cannnot patch wrong apk.
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("Robust补丁1");
//we recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar
//LocalPath是存储原始的补丁文件,这个文件应该是加密过的,TempPath是加密之后的,TempPath下的补丁加载完毕就删除,保证安全性
//这里面需要设置一些补丁的信息,主要是联网的获取的补丁信息。重要的如MD5,进行原始补丁文件的简单校验,以及补丁存储的位置,这边推荐把补丁的储存位置放置到应用的私有目录下,保证安全性
// String localPath=Environment.getExternalStorageDirectory().getPath()+ File.separator+"0Rotbust"+File.separator + "patch";
String localPath=Environment.getExternalStorageDirectory().getPath()+ File.separator+"0Rotbust"+ File.separator + "patch";
patch.setLocalPath(localPath);
// patch.setTempPath(localPath);
Log.w("robust","localPath :" + localPath);
//setPatchesInfoImplClassFullName 设置项各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是和xml配置项patchPackname保持一致,而且类名必须是:PatchesInfoImpl
//请注意这里的设置
patch.setPatchesInfoImplClassFullName("com.lysoft.robustdemo.PatchesInfoImpl");
List patches = new ArrayList();
patches.add(patch);
return patches;
}
/**
*
* @param context
* @param patch
* @return
*
* you can verify your patches here
*/
@Override
protected boolean verifyPatch(Context context, Patch patch) {
//do your verification, put the real patch to patch
//放到app的私有目录
patch.setTempPath(context.getCacheDir()+ File.separator+"robust"+File.separator + "patch");
//in the sample we just copy the file
try {
copy(patch.getLocalPath(), patch.getTempPath());
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("将源修补程序复制到本地修补程序时出错,路径中没有执行修补程序 "+patch.getTempPath());
}
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();
}
}
/**
*
* @param patch
* @return
*
* you may download your patches here, you can check whether patch is in the phone
*/
@Override
protected boolean ensurePatchExist(Patch patch) {
return true;
}
}
public class RobustCallBackSample implements RobustCallBack {
@Override
public void onPatchListFetched(boolean result, boolean isNet, List patches) {
Log.d("RobustCallBack", "onPatchListFetched result: " + result);
Log.d("RobustCallBack", "onPatchListFetched isNet: " + isNet);
for (Patch patch : patches) {
Log.d("RobustCallBack", "onPatchListFetched patch: " + patch.getName());
}
}
@Override
public void onPatchFetched(boolean result, boolean isNet, Patch patch) {
Log.d("RobustCallBack", "onPatchFetched result: " + result);
Log.d("RobustCallBack", "onPatchFetched isNet: " + isNet);
Log.d("RobustCallBack", "onPatchFetched patch: " + patch.getName());
}
@Override
public void onPatchApplied(boolean result, Patch patch) {
Log.d("RobustCallBack", "onPatchApplied result: " + result);
Log.d("RobustCallBack", "onPatchApplied patch: " + patch.getName());
}
@Override
public void logNotify(String log, String where) {
Log.d("RobustCallBack", "logNotify log: " + log);
Log.d("RobustCallBack", "logNotify where: " + where);
}
@Override
public void exceptionNotify(Throwable throwable, String where) {
Log.e("RobustCallBack", "exceptionNotify where: " + where, throwable);
}
}
在这里要说一下
RobustCallBack 只是一个对是否成功加载补丁包的一个回调 你在这里可以看到加载补丁包的成功或者错误的信息
完成以上的步骤基本的配置就算是完成了 接下来就是如何打release包和打补丁了
按照平时打包的时候的流程
在生成 apk 的时候使用 apply plugin:'robust',该插件会生成打补丁时需要的方法记录文件 methodMap.robust,该文件在打补丁的时候用来区别到底哪些方法需要被修复,所以有它才能打补丁。而上文所说的还有 mapping.txt 文件,该文件列出了原始的类,方法和字段名与混淆后代码间的映射。这个文件很重要,可以用它来翻译被混淆的代码。但也不是必须的,如果不需要混淆,可以不保留。这两个文件在生成apk后,分别在 build/outputs/robust/methodsMap.robust,build/outputs/mapping/mapping.txt(需要开启混淆后才会出现),在 app 目录新建个叫 robust 的文件夹,我们需要自己分别拷贝到 app/robust 下,
接下来就是如何打补丁jar包了
这个时候你可以随便修改下你的 代码让补丁包和上面打出的包内容有所改动,
比如你在这里要新增一个方法 或者类,你就需要在新增的方法或者类 上面加一个
@Add 注解
修改代码,在改动的方法上面添加@Modify
注解,对于Lambda表达式请在修改的方法里面调用RobustModify.modify()方法
@Modify
做完上面的修改之后接下来就是如何打补丁包了,首先修改app下面的build.gradle
然后重复打包步骤 等一小会之后会提示你打包失败但是这个时候你要去看具体的错误信息 如果你看到如下信息就证明打包成功了
打出来的补丁包在这里
到这一步基本上都已经完成了所有的步骤了
你现在可以把你第一次打的包安装到你的测试机上看下加载补丁包之前的样子,然后 你需要把补丁包放到如下的路径里,当然你也可以自定义路径
然后你再回到你的app里面 去点击某个按钮去加载你的补丁jar 包 这个时候 你之前随便修改的内容就会替换掉原来的内容或者不存在的内容
到这里基本上所有的步骤都已经完成 如有疑问可以加我个人QQ1778934152 一块探讨学习