Android热修复技术原理及热修复集成

一 .热修复原理

       最近一段时间因为需求变化较大,觉得发版比较麻烦,就了解了一下热修复技术。它更多适用于刚发出去的包有Bug需要紧急修复的时候会用到。即以修复Bug的角度出发,在不需要二次安装下修复已知的Bug。

了解完热修复的应用场景后就得了解它的原理。首先要认识几个关键的词。

ClassLoader:用于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对象,所以整个修复过程就已经形成:

1.DexPathList的构造函数,就是将参数中传递进来的程序文件(就是补丁文件)封装成Element对象,并将这些对象添加到一个Element的数组集合dexElements中去。

2.ClassLoaer 的加载机制是双亲委托机制,在这种机制下,一个Class只会被加载一次。将一个具体的类(class)加载到内存中其实是由虚拟机完成的通过DexClassLoader查找一个类,最终就是就是在一个数组中查找特定值的操作。

3.当代码中的某一个类或者是某几个类有bug,那么我们可以在修复完bug之后,可以将这些个类打包成一个补丁文件,然后通过这个补丁文件封装出一个Element对象,并且将这个Element对象插到原有dexElements数组的最前端,这样当DexClassLoader去加载类时,优先会从我们插入的这个Element中找到相应的类,虽然那个有bug的类还存在于数组中后面的Element中,但由于双亲加载机制的特点,这个有bug的类已经没有机会被加载了,这样一个bug就在没有重新安装应用的情况下修复了。

一个完整简单的修复流程就形成了。当然完成之后就是一个简单的热修复框架了。总结成流程图如下:

附:如需动态修复Bug则需要将补丁文件classes2.dex放在SD目录下。

二.热修复框架比较

先放张图比较一下用的比较多的几个热修复框架:

再者是阿里系的方案对比以及:

下面来介绍几个主要用到的热修复方案:

QQ空间超级补丁技术(dex插桩方案):类修复

实现原理:超级补丁技术基于DEX分包方案,使用了多DEX加载的原理,大致的过程就是:把BUG方法修复以后,放到一个单独的DEX里,插入到dexElements数组的最前面,让虚拟机去加载修复完后的方法。流程如下:

优势:

  1. 没有合成整包(和微信Tinker比起来),产物比较小,比较灵活
  2. 可以实现类替换,兼容性高。(某些三星手机不起作用)

不足:

  1. 不支持即时生效,必须通过重启才能生效。
  2.  为了实现修复这个过程,必须在应用中加入两个dex!dalvikhack.dex中只有一个类,对性能影响不大,但是对于patch.dex来说,修复的类到了一定数量,就需要花不少的时间加载。对手淘这种航母级应用来说,启动耗时增加2s以上是不能够接受的事。
  3.  在ART模式下,如果类修改了结构,就会出现内存错乱的问题。为了解决这个问题,就必须把所有相关的调用类、父类子类等等全部加载到patch.dex中,导致补丁包异常的大,进一步增加应用启动加载的时候,耗时更加严重。

 Andfix:底层替换方案

实现原理:底层替换方案是在已经加载了的类中直接替换掉原有方法,是在原来类的基础上进行修改的。因而无法实现对与原有类进行方法和字段的增减,因为这样将破坏原有类的结构。在Native修改Filed指针的方式,实现方法的替换,达到即时生效无需重启,对应用无性能消耗的目的。其依赖虚拟机进行修改,而AndFix里的ArtMethod是写死的,厂商改掉则会出现问题。

对于实现方法的替换,需要在Native层操作,经过三个步骤:

优势:

  1. BUG修复的即时性
  2. 补丁包同样采用差量技术,生成的PATCH体积小
  3. 对应用无侵入,几乎无性能损耗

不足:

  1. 不支持新增字段,以及修改<init>方法,也不支持对资源的替换。
  2. 由于厂商的自定义ROM,对少数机型暂不支持。

 

Tinker:类加载

实现原理:Tinker针对QQ空间超级补丁技术的不足提出了一个提供DEX差量包,整体替换DEX的方案。主要的原理是与QQ空间超级补丁技术基本相同,区别在于不再将patch.dex增加到elements数组中,而是差量的方式给出patch.dex,然后将patch.dex与应用的classes.dex合并,然后整体替换掉旧的DEX文件,以达到修复的目的。

而其实现的流程图如下:

优势:

  1. 合成整包,不用在构造函数插入代码,防止verify,verify和opt在编译期间就已经完成,不会在运行期间进行。
  2. 性能提高。兼容性和稳定性比较高。
  3. 开发者透明,不需要对包进行额外处理。

不足:

  1. 与超级补丁技术一样,不支持即时生效,必须通过重启应用的方式才能生效。
  2.  需要给应用开启新的进程才能进行合并,并且很容易因为内存消耗等原因合并失败。
  3.  合并时占用额外磁盘空间,对于多DEX的应用来说,如果修改了多个DEX文件,就需要下发多个patch.dex与对应的classes.dex进行合并操作时这种情况会更严重,因此合并过程的失败率也会更高。

 

Sophix(推荐):(类加载+底层替换)

这是我比较推荐的热修复方案,也是我目前项目里用到的。

实现原理:单纯小修改使用的是底层修改方案,代码超过底层替换限制,则使用类加载替换,而且还使用的是补丁包差量技术,可以使patch体积更小。而so库的修复本质上是对native方法的修复和替换。详细说明即是:补丁生成阶段,补丁工具会根据实际代码变动情况进行自动选择,针对小修改,在底层替换方案限制范围内的,就直接采用底层替换修复吗,这样可以做到代码修复即时生效。而对于代码修改超出底层替换限制的,会使用类加载替换,这样虽然及时性没那么好,但最后也可以达到热修复的目的。另外,运行时阶段,Sophix还会再判断所运行的机型是否支持热修复,这样即使补丁支持热修复,但由于机型底层虚拟机构造不支持,还是会走类加载修复,从而达到最好的兼容性。

实现的过程如下图所示:

优势:

  1. 补丁即时生效,不需要应用重启;
  2. 补丁包同样采用差量技术,生成的PATCH体积小;
  3. 对应用无侵入,几乎无性能损耗;
  4. 修改AssetManager的引用处,替换更快更完全。(对比Instanat Run以及所有copycat的实现)
  5. 不必下发完整包,补丁包中只包含有变动的资源。(对比Instanat Run、Amigo等方式的实现)
  6. 不需要在运行时合成完整包。不占用运行时计算和内存资源。(对比Tinker的实现)

不足:

    没有开源而且用户量大了之后开始收费。

 

以上就是对于热修复原理的实现和方案的介绍,希望对大家学习热修复有一定的帮助。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章