1. 为什么需要热修复?
此外,如果有一些紧急需要上线的小需求,希望马上能上线的,也可以借助热修复快速迭代,当然也不能过于依赖热修复。
因此总结一下,它有几大优势
(1)不依赖发版。
(2)用户无感知,无需下载最新应用。
(3)修复成功率高,能快速把损失降到最低。
2. 热修复的原理
修复可以分成三种目标,分别是代码修复、资源修复以及so库修复。
代码修复是我们最经常使用的场景。代码修复又可以分为三种解决方案底层替换方案、类加载方案和Instant Run方案。
2.1类加载方案
类加载方案,基于dex分包方案,具体原理这里还没有进一步学习,大致就是一个dex文件(这个在后面再介绍什么是dex文件)能加载的最大方法数有上限,最多65536个,而一个app随着功能的增加,很可能会超过限制,因此出现了dex分包方案。也就是将一个应用分成多个dex文件,将应用启动必须所需的类放在主dex中,当应用启动时先加载主Dex,再动态的加载次Dex,从而缓解了主Dex的65536限制。基于这个原理,因此有了可以操作的空间。
类加载方案而用很简单的话来说就是,让app加载我们修复好的类,而不是之前出bug的类,自然就能修复bug了。
由于Android虚拟机的是Dalvik ,并不是标准的java虚拟机,而在java虚拟机中通常是将.class文件加载就行了。而在Android 中 加载顺序是 .java->.class->.dex ,dex文件才是最终能被Dalvik执行的文件。
Android 中的类加载器是DexClassLoader和PathClassLoader而不是java中的三个系统加载器,这两者的区别在于PathClassLoader只能加载dex文件或者是已经安装的apk文件(已经安装的apk里面包含了已经解压好的dex文件),而不能从zip中解压出dex文件,而DexClassLoader可以支持.apk、.jar、.dex文件。
当虚拟机在加载时,会遍历一个存放所有dex文件的集合,按照一定的顺序进行加载。当一个类的dex文件被加载后,后面的dex文件如果还有的这个类就不会被执行了。因此我们的热修的原理也很简单,就是把修复好的dex文件放在有bug的dex文件之前,这样就能实现基于类级别的修复,也就是插桩热修的方式。如下图所示
这种方案是不能及时生效的,需要app重启,重新加载dex文件。
2.2底层替换方案
和类加载方法不同的是,不会重新加载新类,而是直接native层修改原有类。这种方式限制较多,不能增减原有类的方法和字段,这样子会改变方法和字段的索引数,导致无法找到正确的方法。因此只能够替换方法。
通过反射找到方法对应的artMethod指针,这个指针所指向的结构体包含了java方法的所有信息比如入口、所属类等等。因此想要替换执行的方法,只需要修改artMethod指针就可以将bug的方法不执行,而去执行修复的方法。
整体的修复流程如下所示。
AndFix是只替换ArtMethod结构体中的字段,这样会有兼容问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败。
而我们所使用的Sophix采用的是替换整个ArtMethod结构体,这样不会存在兼容问题。
2.3 Instant Run
分为三种热插拔(Hot Swap)、温插拔(Warm Swap)、冷插拔(Cold Swap)。三种的区别在于热插拔只针对方法内逻辑的修改,温插拔适合资源的增减,需要重启当前activity生效,冷插拔一般主要针对类结构改变。
在ASM构建apk的时候,会为每个方法注入一个change的字段。如果change为空,就执行原有逻辑,如果不为空,就会执行新的被替换的类中修改的方法。