背景
因为Robust停止更新了,所以这里自己对源码做部分修改:
1、为了支持gradle高版本和Android11,这边做的简单处理就是移除计算ApkHash,demo中gradle版本为7.1.3
2、fix add方法修复失败
3、add新类时打patch包失败,所以d8替换dx打patch包,但碰到lambda无法打patch包,不知道是d8命令出错还是javassist的问题,有大佬能搞定吗,临时方案是通过modify所在方法+add新类代替lambda
目前测试了android12,混淆和非混淆下,modify修复方法,add新增方法和类正常。 问题的话对于kotlin某些语法是不支持的,比如协程;还有和其他插件可能存在兼容问题。
使用方法
github地址:github.com/ccchenhao/R…
-
在App的build.gradle,加入如下依赖
apply plugin: 'com.android.application' .... //制作补丁时将这个打开,这两插件放其他插件后面 //apply plugin: 'auto-patch-plugin' apply plugin: 'robust' -
在整个项目的build.gradle加入classpath
buildscript { repositories { maven { url 'https://jitpack.io' } } dependencies { classpath 'com.github.chenhaomr.RobustFix:gradle-plugin:1.0.0' classpath 'com.github.chenhaomr.RobustFix:auto-patch-plugin:1.0.0' } } -
使用
这个使用和官网的使用是一样的,不过这里再说下。
1、拷贝:demo中的PatchManipulateImp,RobustCallBackSample和patch module到自己目录下, 如果只拷贝patch module的所有java文件,不要忘了添加注解依赖:
api 'com.github.chenhaomr.RobustFix:autopatchbase:1.0.0'将demo中app下的robust.xml配置文件和robust整个文件夹拷贝一份到自己的app目录下
1.1如果想debug环境热修复,forceInsert=true
1.2 hotfixPackage中修改需要插桩的包名
2、打开apply plugin: 'robust',安装apk成功即插桩完成。3、保存第二步运行结果中app/build/outputs/robust/methodsMap.robust和app/build/outputs/mapping/release/mapping.txt到robust目录下。
如果没有开启混淆(对应proguard得设为false),发现没有混淆时不需要mapping文件也可以,但官网说需要。
如果开启了混淆,gradle 3.6及以上版本默认启用R8,会将插入的ChangeQuickRedirect变量优化掉,需要在混淆文件proguard-rules.pro中加入以下代码。 -keepclassmembers class **{ public static com.meituan.robust.ChangeQuickRedirect *; }
4、打开apply plugin: 'auto-patch-plugin' ,使用修复看下面,再运行
就说明patch.dex成功生成,在/build/outputs/robust/patch.jar。
5、测试:将patch包保存到项目的外部存储目录中,自己项目注意命令修改
adb push /Users/chenhmr/Documents/Robust/app/build/outputs/robust/patch.jar /storage/emulated/0/Android/data/com.meituan.robust.sample/cache/robust/patch.jar6、调用new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new RobustCallBackSample()).start();进行热修复
将robust.xml的patchLog设为true有助于排查问题
使用修复
1、对于新添加的方法,使用@Add注解新方法即可,所在类的某个方法必须有modify注解(即使不用随便注解个),否则无法生成patch类。
2、对于修改方法,使用@Modify注解方法或方法内部加RobustModify.modify,要注意修复的到底是方法还是lambda中的代码
2.1对于修复java lambda中的,可以直接lambda中RobustModify.modify()
2.2对于修复kotlin lambda中的,因javassist问题无法编译,所以只能通过@modify lambda所在方法+@Add新类代替lambda。
2.3对于modify方法中的,@modify所在方法,但要注意的是如果该方法中有java lambda和kotlin lambda,打patch包有问题的,目前临时解决方案是也是用2.2方式代替。
注意事项和碰到的问题
问题:robust插件发现插桩失败
1、robust.xml的turnOnRobust,forceInsert有没有开启
2、robust.xml的hotfixPackage是否有需要插桩的包名开头
3、只有一行代码的方法没插桩是正常的
问题:@modify打patch包失败
methodsMap.robust没包含这个方法,可能这个方法是没被插桩或者重新运行了但methodsMap.robust没重新拷贝到robust下。
没插桩这个方法的原因看上面。
问题:d8打patch包失败
说明modify方法中有java lambda或kotlin lambda,解决是modify+add新类代替
其他注意事项可以看官网Wiki
原理
robust插件以map方式(value是自增long,就叫methodNumber)保存所有方法(key)到methodsMap.robust文件,,然后对需要的方法进行插桩,插桩的参数中携带这个方法反射需要的参数和methodNumber。
patch包中包含多个xxPatch类,对应有多个xxPatchControl类和一个PatchesInfoImpl类。
auto-patch-plugin插件根据注解找出所有需要修改方法或新增方法或类,对于修改方法构建对应的patch类,patch类中包含同名修改方法,该方法的原来逻辑全部改成反射处理。
再来看下PatchesInfoImpl
PatchesInfoImpl包含被修复类xx和xxPatchControl类的映射关系,然后修复时classLoader反射实例化xxPatchControl,将这个值赋值给被修复类xx的静态字段changeQuickRedirect,通过changeQuickRedirect不为null说明这个类是被修复类。
robust插桩的PatchProxy.isSupport->changeQuickRedirect.isSupport->xxPatchControl.isSupport(),这个isSupport方法内部找到该xxPatch类所有修改方法的methodNumber,然后看是否包含当前方法的methodNumber,是的话就说明是修复方法,最终执行xxPatchControl.accessDispatch,accessDispatch方法中实例化对应的xxPatch,并调用对应的modify方法。
更多细节看官方介绍tech.meituan.com/2016/09/14/…