热修复方案
热修复指的是在不发版的前提下修复代码问题。注意,Android上无法实现类卸载。但在Android 8已经可以重新加载类了。详见Android Studio Apply Changes
- 热启动热Java层修复
- 热启动底层替换修复
- 冷启动热修复
- 资源热修复
整体来说,要么对类加载器着手(反射获取pathList,增加patch.dex到队头,或更改dex元素),要么对JVM着手(修改方法指针),要么插桩(AOP),要么可以直接支持重新加载。
热启动修复
Java层热启动修复
提前在修复点打桩,根据后台下发修复情况,下载不同的远程dex等文件,下载完成后即可加载,变更前端逻辑。加载调用协议需要提前规定设计。(Robust)
底层热启动修复
以art虚拟机为例,依靠jni,通过更改artMethod信息,即代码的访问权限、执行地址等,使得在不破坏原有代码方法调用上,使用了新的方法实现等。
- Andfix使用此方案
- 如果厂家定制了不同的art实现,那么此方案会出现兼容性问题
- 实际上有一些后来的Hook方案,不依赖类似Xposed框架,其实现原理也是如此,通过jni对artMethod进行修改,在hook点上调用了自己的Hook方法。
冷启动热修复
- 也可以同前面第一个方案,下载远程的dex文件后,在下次启动时加载。
- 修复方案提供差量dex文件,修复方案通过将其与当前的dex合并,之后在下次启动时即可加载了新的dex文件。(Tinker)
资源热修复
可参见插件化方案理解中的资源插件方案。
InstantRun vs Apply Changes
增加gradle transform task,对所有类、application做转化。每个类都增加了一个IncrementalChanges成员变量,每个类的方法都会检查这个成员变量,若不为空,则调用它的access$dispatch方法,参数1是方法签名string,参数2是参数列表数组。application启动时同时启动local socket和android studio通信,以此获取changes list。获取后,如果是热更新(android studio负责记录分析哪些代码发生变化,做增量编译,以及控制更新模式),那么app server 通过socket获取到变更后,加载该类(一般补丁类的名称是原类的名称+$override。这里考虑如果多次补丁,那么为了让其能加载新的同名类,实际上每次加载的时候都需要生成新的classloader加载(因为不同的classloader加载的类被认为不同,而在转化过的application中,也对classloader包装了一层,添加了一层委托,在这层中从而来控制overide的类加载问题。这里有很多classloader上要注意的技巧)。最终对目标类的IncrementalChanges成员变量赋值。这个主要是hot swap的情况。
其他的warm swap,cold swap类似。可以查看原文。
借助安卓8类重新加载+数据库dex类缓存校验apk差异类+delta push