第十三章 热修复原理

第十三章 热修复原理

13.1 热修复的产生

  • 刚发布的版 出现了严重的 Bug ,这就需要去解决 Bug 测试并打包在各个应用市场上重新发布,这会耗费大量的人力和物力,代价比较大。
  • 已经改正了此前发布版本的 Bug,如果下 个版本是一个大版本, 那么两个版本的 间隔时间很长,这样要等到下个大版本发布再修复 ,此前版本的 Bug 会长期地影响用户。
  • 版本升级率不高,并且需要很长时间来完成版本覆盖,此前版本的 Bug 就会一直影响不升级版本的用户。
  • 有一个小而重要的功能,需要短时间内完成版本覆盖,比如节日活动。

13.2 热修复框架的种类和对比

部分热修复框架:

image20220307204613520.png

部分热修复框架对比:

image20220307204646042.png

13.3 资源修复

13.3.1 Instant Run

是 Android Studio 2.0 以后新增的一个运行机制,能够减少开发人员第二次及以后的构建和部署时间。

传统编译部署过程:

image20220307204947260.png

Instant Run 的部署过程:

image20220307205027341.png

  • Hot swap:效率最高的部署方式,不用重启 Activity
  • Warm Swap:效率次之,需要重启 Activity。修改或删除资源时需要使用。
  • Cold Swap:App 需要重启,但是不要重新安装。当添加、删除或修改一个字段和方法或者添加一个类等时候需要。

13.3.2 Instant Run 的资源修复

该过程不是 Android 的源码,是 Android Studio 中执行的过程,过程为两个步骤:

  1. 创建新的 AssetManager,通过反射调用 addAssetPath 方法加载外部资源。
  2. 将 AssetManager 类型的 mAssets 字段的引用全部替换为新的 AssetManager。

13.4 代码修复

13.4.1 类加载器方案

类加载方案基于 Dex 分包方案,而 Dex 分包方案主要是为了解决以下两个问题:

  1. 65536 限制

    DVM Bytecode 的限制,DVM 指令集中的方法调用的指令 invoke-kind 索引为 16 bits,因此最多只能引用 65535 个方法。

  2. LinearAlloc 限制

    DVM 中的 LinearAlloc 是一个固定的缓存区,当方法数超出了缓存区的大小时会报错。

Dex 分包方案主要是将代码分成多个 Dex。启动时只加载主 Dex,然后再动态加载次 Dex。

Dex 分包主要有两种方案,Google 官方方案与 Dex 自动拆包和动态加载方案。这里不做说明。

代码修复中的类加载器方案主要是在新的 Dex 中放入补丁包代码,然后再 ClassLoader 中,将新的文件对应的 Element 放到 Element 数组第一个位置,更由于双亲委派机制,旧的类将不会被加载。

image20220307210243986.png

先找到新的 dex ,然后先加载,这样原本的加载过程就会被跳过。

此方案需要重启 App 生效,因为类是不允许卸载的,原本已经加载的原本的类无法卸载。

  • QQ 空间的超级不定和 Nuwa 就是使用以上国产,直接将补丁包放在 Element 数组的第一个元素得到优先加载。
  • 微信 Tinker 在 QQ 空间超级补丁的基础上做了 diff,将新旧 APK 做了 diff 得到 patch.dex 进行下发,然后手机端在将 patch.dex 与原本的 classes.dex 做合并,然后运行时反射将 classes.dex 放在第一个元素(直接将新的一整个 classes.dex 作为第一个元素,而不是和之前的只将补丁包放入)。
  • 饿了么的 Amigo 则是将补丁包中每个 dex 对应的 Element 取出来,组成新的数组,在运行时通过反射替换原有的整个数组。

以上运行时只是方便理解,实际上原本的启动的代码里就有判断是否需要热修复的逻辑,直接讲文件放到对应位置或者都在对应位置做出标记则可引导 app 启动时加载补丁包。

13.4.2 底层替换方案

ART 虚拟机中会存所有 java 方法信息的 ArtMethod 结构体,包括执行入库、访问权限、所属类和执行地址等。通过直接在运行时替换该结构体,可以坐待代码热修复,将原本的方法调用定向到新的方法调用。

AndFix 采用的是替换 ArtMethod 结构体中的字段,这样会有兼容问题,因为厂商可能会修改 ArtMethod 结构体,导致方法替换失败。 Sophix 采用的是替换整个 ArtMethod 结构体 ,这样不会存在兼容问题。

这种方式可以直接生效,不需要重启。

13.4.3 Instant Run 方案

主要是使用 ASM 实现,在每个方法中插入代码,根据是否需要替换来实现方法调用的替换。

使用该方案的热修复框架有 Robust 和 Aceso。

13.5 动态链接库的修复

so 库的修复的基础原理就是 加载 so。主要有两种方案:

  1. 将 so 补丁插入到 NativeLibraryElement 数组的前部,让 so 补丁的路径先被返回和加载。
  2. 调用 System 的 load 方法来接管 so 的加载入口。