Robust是美团点评团队在2017年3月开源的热修复框架,和阿里的AndFix不同,Robust不用依赖JNI层,直接通过Java层代码就可以实现热修复。相比于其他热修复框架,官方给出Robust的优势有以下几点
- 支持Android2.3-7.X版本
- 高兼容性、高稳定性,修复成功率高达三个九
- 补丁下发立即生效,不需要重新启动
- 支持方法级别的修复,包括静态方法
- 支持增加方法和类
- 支持ProGuard的混淆、内联、优化等操作
不接触JNI层,Robust是如何添加方法与类、立即生效其补丁的呢?
Robust一共分为四个模块,分别为:
- autopatchbase(热补丁基类)
- gradle-plugin(负责apk包的插桩)
- auto-patch-plugin(负责提取制作patch包)
- patch(负责补丁包的补丁工作)
我们一个一个来分析
AutoPatchBase
作为热补丁的基类,主要类是有几个:
2个注解分别为@Add
(添加新的类)和@Modify
(修改当前类的方法);
一个Constant
类用来保存固定的字符串;
一个ChangeQuickRedirect
接口,用来给plugin确认当前类是否需要patch
Gradle-Plugin
用于插桩的工具。首先进行对Apk检查防止包被篡改,然后在RobustTransform.groovy
中
- 执行
apply(...)
方法,读取项目目录下的robust.xml
加载热补丁的配置 - 进入
transform(...)
方法,依次读取bootClasspath
下的所有class文件并加入ClassPool
中 - 进入
insertRobustCode
方法,然后做了以下几件微小的工作:- 将class设置为public
- 当class为接口/无方法类时,执行5
- 给class插入一个public static的
ChangeQuickRedirect
对象 - 对所有方法使用Javassist插入代码:当该方法的
changeQuickRedirect
不为空时,直接将参数直接传入PatchProxy
的accessDispatchVoid
/accessDispatch
方法并返回,这样做跳过了原方法后面的代码,从而实现了方法的替换 - 写入原来的class文件中
- 打包压缩生成apk
由此,就实现了插桩的工作
Auto-Patch-Plugin
制作patch包的工具。主要逻辑在AutoPatchTransform.groovy
中,
- 执行
apply(…)
方法,初始化参数 - 跳到
transform(…)
中,又做了细微的工作- 复制项目中的
LIB_NAME_ARRAY
中的3个jar包到./robust/
文件夹下(unknown why) - 读取
bootClasspath
路径下的class文件并转换为CtClass
对象数组 - 执行打包
autoPatch(…)
- 首先执行
ReadAnnonation(…)
去读取CtClass
数组中的注解,然后把注解的方法/类放在Config
中保存 - 执行
ReadMapping.initMappingInfo()
,读取mapping.txt将被ProGuard混淆了的类的对象还原成原来的类 - 通过
InlineClassFactory
构造新加的类 - 处理
super
的方法调用 - 针对每一个有补丁方法的类,使用
PatchesFactory.createPatch
构造出Patch
实现类 - 使用
PatchesControlFactory.createPatchesControl
构造PatchControl
类 - 使用
PatchesInfoFactory.createPatchesInfo
构造PatchInfo
类 - 重新打包,优化smali
- 首先执行
- 复制项目中的
Patch
在activity中,通过执行以下代码运行了补丁
new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new Callback()).start();
PatchExecutor
是一个Thread
的子类,通过PatchManipulateImp
指定的路径去读patch
文件,然后给DexClassLoader
加载并读取PatchInfo
,然后通过PatchInfo
中的信息获得需要补丁的类,通过反射修改其changeQuickRedirect
对象的值,做到修改函数运行的路径
总结
用一张图来总结robust原理
当然原理看起来简单,其中还是有很多难点在其中,例如
- 如何解决patch中涉及到的包访问权限
- 如何解决super的问题
- …
各位对具体实现有兴趣的,可以通过解压官方demo中的补丁包,用JD-GUI来看看patch包中各种patchInfo
、patchControl
是如何处理的