上篇讲到代码修复技术分为两类:底层替换热修复和类加载热修复。

这篇主要是对这两者底层原理的学习。

一、底层替换热修复原理

1. 即时生效的Andfix

简单概括下Andfix:

众多热修复技术中,Andfix做到了非常炫酷的即时生效功能,无需重启应用就可以修复类中的方法,同时他也存在无法适配某些机型的缺点。

他的核心方法在于replaceMethod函数中:

我们来看一下Andfix热修复的大概流程:

上面能看到,对方法修改完全依赖于ArtMethod指针。而指针完全是通过对虚拟机底层的ArtMethod真实地址进行强转得到的。这造成了一个非常麻烦的问题。

Andfix底层强转使用的ArtMethod对象并不是真机中底层的ArtMethod类,而是根据Android开源的代码,自己写死的,和原生一模一样的类。

如果,厂商修改了底层的ArtMethod代码,两者就会不匹配,替换机制就会出现问题。

Andfix替换机制

Andfix的替换本质是对方法的逐一替换,并且只能平行替换,一旦虚拟机底层ArtMethod方法数产生改变,就会产生“错位”,无法进行热修复。

所以,Sophix为了解决这一问题,采用了将底层ArtMethod全部替换的方式。

Sophix替换机制

2. Sophix的化繁为简

Sophix将替换方法,浓缩成了下面一句话:

ArtMethod替换方法

这句话的意思是从dmeth(内存源地址)拷贝sizeof(ArtMethod)到smeth(目标起始地址),显然搞定这句话的难点在于如何获取ArtMethod的大小。如果ArtMethod的size出现误差,热修复就肯定失败了,会出现不可预期的错误。

那么,如何在应用层获取ArtMethod的正确大小呢?

art初始化类时会给类中所有的方法分配空间

他的底层是一个数组,通过ptr指针给ArtMethod分配空间

由于ArtMethod在art中是紧密排列的,这样就能通过自己构建一个类,里面拥有两个静态方法,去计算两个静态方法之间的差值,从而获得ArtMethod的大小。

这样我们就能完美替换每个ArtMethod了。这种替换方式完全可以忽视各个厂商底层的差异,包括所有Android版本的差异,兼容和稳定性都非常好。但是同时也会引发一些问题。

访问权限问题

方法调用时权限检查

构造函数在调用同一个类的私有方法时,没有做任何检查,也就是说调换方法之后,只要确认两个方法属于同一个类,就不会产生权限问题。

同名包下的权限问题

在替换类的时候,补丁包和原包所用的ClassLoader不是同一个ClassLoader,导致判别两者包名不一致,出现没有访问权限的问题。

抛出异常:

源码中的classLoader判断:

解决方法:将原包的ClassLoader赋值给补丁包的ClassLoader

反射调用非静态方法

反射过程中,底层会调用InvokeMethod,在反射的过程中会去验证调用方法的对象和方法所属的对象,如果其中有一项不匹配就会抛出异常

静态方法就不会出现类似问题,因为调用静态方法是不需要对象实例作为参数的。

目前,这种热修复引起的反射问题,只能通过冷启动修复解决。

3. 底层替换热修复的痛点

1)只能替换方法,对方法数有严格限制,替换类中对已经存在的方法不能增加或者减少。当然新添加类,不会产生问题。

2)修复后的类中方法,被反射调用。

二、冷启动修复原理

1.冷启动修复简述

冷启动修复很好的弥补了底层替换热修复的痛点,不仅替换类的方法数不受限制,同时反射也能正常调用非静态方法。很合适和底层替换热修复配合使用。