因为工作需要,所以需要在公司项目中增加Robust补丁,其实操作流程很简单,只是对其中原理不是很理解,下面简单看一下生成的文件:
一、首先看一下添加Robust依赖之后生成的apk文件的代码:
经过查看可以了解到,Robust给每一个类(另一个MainActivity类也增加了)增加了一个叫做ChangeQuickRedirect的静态变量,然后在每一个方法的最前面增加了一段代码,目前我们还不知道这段代码的含义,但是大概知道,如果能够PatchProxy.isSuuport()为true的话,那么就不走原来的逻辑,而是直接走插桩代码的逻辑。
下面简单看一下PatchProxy.isSupport里面有什么:
经过上面代码,其实主要有两个疑问:
- 如何将那一段插桩代码插入进去?
- 每个类中都会插入一个ChangeQuickRedirect静态变量,在走到具体方法时,会在isSupport里面判断该变量是否为空,那么是什么时候对这个ChangeQuickRedirect变量赋值的呢?
二、下面看一下补丁patch.jar(patch.dex)代码:
打开之后一共有三个类:
1、PatchesInfoImpl类:
可以看到里面逻辑很简单,是将待修复的方法所在类与另一个XXXXPatchControl类放进了集合:
2、下面看一下SecondActivityPatchControl类:
首先可以看到这个类实现了ChangeQuickRedirect接口,并重写了isSupport和accessDispatch方法:
重写了isSupport方法:(内部逻辑没太看懂,但是应该只是判断一下当前方法是不是要fix的那个方法,需要去撸源码看一下)
重写了accessDispatch方法,因为是反编译代码,所以其实没太看懂,但是起码可以看到重新new了SecondActivityPatch对象,并且执行了其中的fix方法,所以应该来说最终真正的补丁后的方法在SecondActivityPatch中:
3、下面看一下最后一个类:SecondActivityPatch
看完了上面补丁里面的代码,其实还是有一个最开始的疑问,这个patch补丁是如何加载以及生效的?因为代码被混淆,我们只好去看一下Robust的wiki:
wiki说明是因为如何加载需要开发者自定义,因此官方给出了一个demo,在demo中可以看到,逻辑比较简单,主要有两步:
(1)、联网下载补丁;
(2)、使用DexClassLoader加载patch补丁,加载时,首先反射获取com.meituan.robust.patch.PatchesInfoImpl的对应的Class对象,通过调用其中的getPatchedClassesInfo()方法来查看哪些类有修改,获取patchClass以及oldClass,同时使用反射修改oldClass中对应的变量值ChangeQuickRedirect,改成XXXXPatchControl对应的Class对象,从而保证patch设置的状态成功;
三、通过上面的逻辑梳理,基本可以确定Robust逻辑是这样的:
1、在编译时候通过Gradle插件或者AOP对每一个类的每个方法进入了代码插桩,插入固定的一段代码,并给每一个类都添加一个ChangeQuickRedirect静态变量;
2、通过对需要fix的方法增加注解,然后生成Patch补丁,补丁中包含三个类:
- PatchesInfoImpl:包含oldClass类名以及patchClass类名,放入集合;
- XXXXPatch:fix之后的方法放进了这个类,不会处理其他逻辑;
- XXXXPatchControl:实现了ChangeQuickRedirect接口,其实是XXXXPatch类的代理,重写isSupport和accessDispatch方法,在accessDispatch方法中会进入XXXXPatch类的fix之后的方法;
3、启动apk之后,下发patch补丁,使用类加载器加载补丁,同时读取PatchesInfoImpl类中的oldClass以及PatchClass,修改oldClass中的ChangeQuickRedirect变量,指向为XXXXPatchControl对象;
4、走到等待fix的方法时,因为此方法待fix,所以会直接走进ChangeQuickRedirect的实现类XXXXPatchControl类,同时执行对应的accessDispatch方法,从而最终执行XXXXPatch的对应方法。
其实官方图画的相当清楚:
四、虽然整体的流程大概明白,但是其中有很多疑问没有解决:
1、如何插桩那一段代码?(ASM ?Javassist?)
2、补丁如何自动生成?
3、如何能够准确找到每一个方法?以及下面这一句代码的具体含义?
原始代码: