Android热修复技术(二)------代码修复之底层替换原理

1、Andfix是怎么实现的热修复??

由于Java层的每一个方法在虚拟机实现里面都对应着一个ArtMethod的结构体,只要把原方法的结构体内容替换为新的结构体的内容,在调用原来方法的时候,真正执行的指令是新方法的指令,就是就可以实现热修复。
Andfix采用的方法是在已经加载了的类中直接在native层替换掉原有的方法,是在原来类的基础上进行修改的,其核心是native方法replaceMethod。不可否认,不同的android版本对应的java底层对应的ArtMethod是不一样的,因此Andfix不得不搞好多不同的case,处理不同的情况,但是每种case里都不外乎是通过env->FromReflectedMethod得到由Method对象得到他对应的ArtMethod的真正的起始地址,把他强转为ArtMethod指针,就可以实现对所有的成员进行修改,也就是旧的干掉,新的加进来。

2、为什么这样替换完就可以实现热修复呢?

android虚拟机调用方法原理

在android6.0中,art虚拟机中ArtMethod的结构包含了很多的信息,其中最关键的就是entry_point_from_interprete_entry_point_from_quick_compiled_code_这两个东西,这两个东西是什么呢?从名字看出,他们是方法的执行入口,我们知道android是先将java代码编译为dex代码,而dex然后呢?art中可以采用解释模式或者AOT机器码模式进行下一步的执行。
如果是以解释模式进行这个方法的解析,那在调用这个方法的时候,就会取得这个方法的entry_point_from_interprete_,然后跳进去执行。如果是AOT,先要将dex预编译成机器码,然后执行,同样一个方法就会直接跳到entry_point_from_quick_compiled_code_中执行。当然在执行的时候还需要用到ArtMethod中的一些别的字段。因此需要进行全部的替换,而不是单单简单的替换一个入口地址就行了 。

3、这种方案的弊端

Andfix写死了ArtMethod结构体,会带来很大的兼容性问题,就像我在前一篇博文中说的那样,有些厂商会更改ArtMethod代码,因此就会出现一些和原来的开源代码不一样的地方,如果这样,那替换机制就挂了。

4、解决方案

从上面我们可以看出,native层面的替换思路,其实就是把ArtMethod中的所有成员替换了个遍,所以阿里新的思路就变为了不再构造出ArtMethod的具体的每个字段,而是来一个整体替换。不再关心里面实现的细节了。也就是浓缩为一句话:

memcpy(smeth,dmeth.sizeof(ArtMethod));


这里写图片描述

变为:
这里写图片描述
最为关键的地方就是这个sizeof,如果计算失误,就会挂!!!
作为app开发者,app会下发到不同的Android设备,所以需要在运行时动态的得到app所运行设备上面的底层ArtMethod的大小,这有点困难。
在art中,初始化一个类的时候会给这个类的所有方法分配空间,其中方法分为direct方法和virtual方法,他们通过AllocArtMethodArray函数分配方法所在区域,并且方法都是一个接一个紧密的new出来排在这个方法数组之中的:
这里写图片描述

由此我们看出一个ArtMethod的大小,就是相邻的两个方法所对应的ArtMethod的起始地址的差值。可以在jni层取得他们地址的差值,从而得到所要的sizeof(ArtMethod)。

5、解决方案带来的问题以及考虑

1、方法调用权限问题

通过前面的述说,我们发现这只是替换了ArtMethod的内容,但是新的方法所属的类是不同的类,能否访问这个类的其他的private方法呢?
通过查看dex和native代码发现,在调用同一类的私有方法的时候,没有做任何的权限的检查。而且在dextoAOT的过程中,生成的机器码出也不存在权限访问的问题,因此这点没问题了

2、同包名下的权限问题

if补丁中的类在访问同包名的类时,会出现访问权限异常。原因是由于替换了的类的方法是从补丁包的ClassLoader加载的,与原来的包不是同一个类加载器,那么就导致两个类无法被判断为同包名,这也就是如何判断两个类是不是一个包的原因!!!
实现起来也比较简单:
这里写图片描述

3、反射调用非静态方法产生的问题

这个问题就是:当一个非静态方法被热替换后,在反射调用这个方法的时候,就会抛出异常。
这里写图片描述
最后一行代码会爆出异常:
这里写图片描述

希望得到的是新的补丁类,而后面实际上调用的是原来的类,这两个不是同一个类,在invoke的时候,系统会调用检查object class的函数,也就是说invoke传入的作用的对象和调用的Method所对应的ArtMethod所属的class要是同一个class,才能通过验证, 这种情况咋办啊,那就采用另一种冷启动的机制来应对了。

4、无法增加或减少方法和字段的个数

这一点在Android热修复技术(一)中已有所提及,但新增一个完整的,原先包里面不存在的新类是完全可以的。

6、总结

对于这种热修复方式,有两种情况是不合适的:
1、对于改变了原有类的结构的修改;
2、对于热修复了原有的非静态方法,并且进行了反射调用;

因此,对于较小程度的修复这种方案是非常不错的能够实时生效无需重启app,并且有着very nice 的设备兼容性,他也可以结合资源修复,做到代码和资源的即时生效。对于比较大的代码改动,以及被修复方法反射调用时,是要重启app的,Sophix中也提供了一种完整的dex修复机制,以后有时间再去学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值