热修复框架研究之Robust原理

热修复框架研究之Robust原理

MarkZhaiMarkZhai

热修复框架研究之Robust原理

作者:Houskii(这是群里重邮的子沛同学的投稿哦)

Robust是美团点评团队在2017年3月开源的热修复框架,和阿里的AndFix不同,Robust不用依赖JNI层,直接通过Java层代码就可以实现热修复。相比于其他热修复框架,官方给出Robust的优势有以下几点

  1. 支持Android2.3-7.X版本
  2. 高兼容性、高稳定性,修复成功率高达三个九
  3. 补丁下发立即生效,不需要重新启动
  4. 支持方法级别的修复,包括静态方法
  5. 支持增加方法和类
  6. 支持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中

  1. 执行apply(...)方法,读取项目目录下的robust.xml加载热补丁的配置
  2. 进入transform(...)方法,依次读取bootClasspath下的所有class文件并加入ClassPool中
  3. 进入insertRobustCode方法,然后做了以下几件微小的工作:
    1. 将class设置为public
    2. 当class为接口/无方法类时,执行5
    3. 给class插入一个public static的ChangeQuickRedirect对象
    4. 对所有方法使用Javassist插入代码:当该方法的changeQuickRedirect不为空时,直接将参数直接传入PatchProxy的accessDispatchVoid/accessDispatch方法并返回,这样做跳过了原方法后面的代码,从而实现了方法的替换
    5. 写入原来的class文件中
    6. 打包压缩生成apk

由此,就实现了插桩的工作

Auto-Patch-Plugin

制作patch包的工具。主要逻辑在AutoPatchTransform.groovy中,

  1. 执行apply(…)方法,初始化参数
  2. 跳到transform(…)中,又做了细微的工作
    1. 复制项目中的LIB_NAME_ARRAY中的3个jar包到./robust/文件夹下(unknown why)
    2. 读取bootClasspath路径下的class文件并转换为CtClass对象数组
    3. 执行打包autoPatch(…)
      1. 首先执行ReadAnnonation(…)去读取CtClass数组中的注解,然后把注解的方法/类放在Config中保存
      2. 执行ReadMapping.initMappingInfo(),读取mapping.txt将被ProGuard混淆了的类的对象还原成原来的类
      3. 通过InlineClassFactory构造新加的类
      4. 处理super的方法调用
      5. 针对每一个有补丁方法的类,使用PatchesFactory.createPatch构造出Patch实现类
      6. 使用PatchesControlFactory.createPatchesControl构造PatchControl类
      7. 使用PatchesInfoFactory.createPatchesInfo构造PatchInfo类
      8. 重新打包,优化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是如何处理的


「真诚赞赏,手留余香」
还没有人赞赏,快来当第一个赞赏的人吧!
文章被以下专栏收录
4 条评论
推荐阅读