上篇讲到代码修复技术分为两类:底层替换热修复和类加载热修复。
这篇主要是对这两者底层原理的学习。
一、底层替换热修复原理
1. 即时生效的Andfix
简单概括下Andfix:
众多热修复技术中,Andfix做到了非常炫酷的即时生效功能,无需重启应用就可以修复类中的方法,同时他也存在无法适配某些机型的缺点。
上面能看到,对方法修改完全依赖于ArtMethod指针。而指针完全是通过对虚拟机底层的ArtMethod真实地址进行强转得到的。这造成了一个非常麻烦的问题。
Andfix底层强转使用的ArtMethod对象并不是真机中底层的ArtMethod类,而是根据Android开源的代码,自己写死的,和原生一模一样的类。
如果,厂商修改了底层的ArtMethod代码,两者就会不匹配,替换机制就会出现问题。
Andfix的替换本质是对方法的逐一替换,并且只能平行替换,一旦虚拟机底层ArtMethod方法数产生改变,就会产生“错位”,无法进行热修复。
所以,Sophix为了解决这一问题,采用了将底层ArtMethod全部替换的方式。
2. Sophix的化繁为简
Sophix将替换方法,浓缩成了下面一句话:这句话的意思是从dmeth
(内存源地址)拷贝sizeof(ArtMethod)
到smeth
(目标起始地址),显然搞定这句话的难点在于如何获取ArtMethod的大小。如果ArtMethod的size出现误差,热修复就肯定失败了,会出现不可预期的错误。
那么,如何在应用层获取ArtMethod的正确大小呢?
art初始化类时会给类中所有的方法分配空间这样我们就能完美替换每个ArtMethod了。这种替换方式完全可以忽视各个厂商底层的差异,包括所有Android版本的差异,兼容和稳定性都非常好。但是同时也会引发一些问题。
-
访问权限问题
-
方法调用时权限检查
构造函数在调用同一个类的私有方法时,没有做任何检查,也就是说调换方法之后,只要确认两个方法属于同一个类,就不会产生权限问题。 -
同名包下的权限问题
在替换类的时候,补丁包和原包所用的ClassLoader不是同一个ClassLoader,导致判别两者包名不一致,出现没有访问权限的问题。
抛出异常:
源码中的classLoader判断:
-
反射调用非静态方法
反射过程中,底层会调用InvokeMethod,在反射的过程中会去验证调用方法的对象
和方法所属的对象
,如果其中有一项不匹配就会抛出异常
静态方法就不会出现类似问题,因为调用静态方法是不需要对象实例作为参数的。
目前,这种热修复引起的反射问题,只能通过冷启动修复解决。
3. 底层替换热修复的痛点
1)只能替换方法,对方法数有严格限制,替换类中对已经存在的方法不能增加或者减少。当然新添加类,不会产生问题。
2)修复后的类中方法,被反射调用。
二、冷启动修复原理
1.冷启动修复简述
冷启动修复很好的弥补了底层替换热修复的痛点,不仅替换类的方法数不受限制,同时反射也能正常调用非静态方法。很合适和底层替换热修复配合使用。