# 热修复

参考[Android 插件化和热修复知识梳理](https://www.jianshu.com/p/704cac3eb13d)

**插件化和热修复**不是同一个概念，虽然站在技术实现的角度来说，他们都是从系统加载器的角度出发，无论是采用hook方式，亦或是代理方式或者是其他底层实现，都是通过“**欺骗**”Android 系统的方式来让**宿主**正常的加载和运行**插件（补丁）**&#x4E2D;的内容；但是二者的出发点是不同的。**插件化**顾名思义，更多是想把需要实现的模块或功能当做一个独立的提取出来，减少**宿主**的规模，当需要使用到相应的功能时再去加载相应的模块。**热修复**则往往是从修复bug的角度出发，强调的是在不需要二次安装应用的前提下修复已知的bug。

## 类加载机制

Android中类的加载也是通过ClassLoader来完成，具体来说就是PathClassLoader 和 DexClassLoader 这两个Android专用的类加载器。

* PathClassLoader：只能加载已经安装到Android系统中的apk文件（/data/app目录），是Android默认使用的类加载器。
* DexClassLoader：可以加载任意目录下的dex/jar/apk/zip文件，也就是我们一开始提到的补丁。

这两个类都是继承自BaseDexClassLoader，我们可以看一下BaseDexClassLoader的构造函数。

```
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
```

这个构造函数只做了一件事，就是通过传递进来的相关参数，初始化了一个DexPathList对象。**DexPathList的构造函数，就是将参数中传递进来的程序文件（就是补丁文件）封装成Element对象，并将这些对象添加到一个Element的数组集合dexElements中去**。

> ClassLoaer 的加载机制是一种特别聪明的方式，**双亲委托机制**，在这种机制下，一个Class只会被加载一次。

总的来说，**通过DexClassLoader查找一个类，最终就是就是在一个数组中查找特定值的操作。**

综合以上所有的观点，我们很容易想到一种非常简单粗暴的热修复方案。假设现在代码中的某一个类或者是某几个类有bug，那么我们可以在修复完bug之后，可以**将这些个类打包成一个补丁文件，然后通过这个补丁文件封装出一个Element对象，并且将这个Element对象插到原有dexElements数组的最前端**，这样当DexClassLoader去加载类时，优先会从我们插入的这个Element中找到相应的类，虽然那个有bug的类还存在于数组中后面的Element中，但由于双亲加载机制的特点，这个有bug的类已经没有机会被加载了，这样一个bug就在没有重新安装应用的情况下修复了。

## 热修复框架和方案

### QQ 空间超级补丁方案 Tinker

策略有所不同，但总的来说都是从上层ClassLoader的角度出发，由于ClassLoader的特点，如果想要新的补丁文件再次生效，无论你是插桩还是提前合并，都需要重新启动应用来加载新的DexPathList。这样就无法在用户神不知鬼不觉的情况下把bug修复了，HotFix在这方面就有绝对的优势了。

### HotFix(即AndFix)

AndFix 提供了一种运行时在Native修改Filed指针的方式，实现方法的替换，达到即时生效无需重启，对应用无性能消耗的目的。

由于他是Native层操作，因此如果我们在Java层中新增字段，或者是修改类的方法，他是无能为力的。同时由于Android在国内变成了安卓，各大手机厂商定制了自己的ROM，所以很多底层实现的差异，导致AndFix的兼容性并不是很好。

### Sophix

采用**全量替换**的思路，从一种更高的层次实现了热修复。应该是现有最成熟的热修复方案了。
