天天看点

腾讯热更新Tinker的故事前言TinkerTinker优缺点QZone总结

热更新系列目录

  1. 热更新你都知道哪些?
  2. 热更新Sophix的爬坑之路
  3. 腾讯热更新Tinker的故事
  4. 阿里热更新Sophix的故事

热更新Tinker

  • 前言
  • Tinker
  • Tinker优缺点
  • QZone
  • 总结

博客创建时间:2020.05.20

博客更新时间:2021.02.23

前言

热修复技术Tinker中的核心是类加载替换,该技术是基于multidex原理的类加载方法,如对类的加载不甚了解,请阅读我的博客 《类加载机制原理解析》 进行科普。本篇博客主要是对腾讯系的Tinker热修复方案进行讲解,同时补充下腾讯系的其他热修复方案。

如不看懂

类加载

的原理,将无法真正理解Tinker的底层实现

Tinker

在Tinker之前腾讯系热修复已有Qzone等技术方案,但是存在这部分重大缺陷,因此Tinker为解决相关缺陷而问世。

Tinker针对Qzone的不足,不再将patch.dex增加到elements数组中,而是差量的方式给出patch.dex,然后将patch.dex与应用的classes.dex合并,然后整体替换掉旧的DEX文件,以达到修复的目的。

核心原理

Tinker的热修复实质就是利用Element[] dexElements的顺序来做文章。 当一个补丁的patch.dex放到了dexElements的第一位。那么当加载一个bug类时,发现在patch.dex中,则直接加载这个类,原来的bug类可能就被覆盖了。 一旦一个类被加载了,将不再重复加载同一个类,也即bug被修复。

热修复过程:

  1. Tinker 方案参考了multidex的实现原理,在编译时通过新旧两个APK的Dex生成差异patch.dex。
  2. 通过相关平台将patch.apk下发到终端,程序运行时,将差异patch.dex重新和旧的dex合并还原成新的.dex。由于合并的过程比较费时,所以有一个单独的PatchService进行合并。
  3. 为了减小patch的大小,Tinker自研了DexDiff算法,深度利用Dex的格式减小差异大小
  4. 程序再次启动后,Tinker是全量合成的新dex插入到dexElements最前面。使得新的.dex中的内容Class优先加载,bug得到修复。

注意

  1. 为了减小patch的大小,Tinker自研了DexDiff算法,深度利用Dex的格式减小差异大小。让patch只包含有差异的部分,相同的部分不包含
  2. Tinker的补丁包形式是.apk格式,除了patch.dex,还包含resource差异包等文件
  3. 由于合并的过程比较费时,所以有一个单独的PatchService进行合并。
  4. 类加载实现原理涉及了dex文件的重新解压缩合并等处理,消耗的内存大,时间长,系统内存低时容易合并失败。也即类加载热更新存在一定的失败率。

Tinker热更新流程

腾讯热更新Tinker的故事前言TinkerTinker优缺点QZone总结
腾讯热更新Tinker的故事前言TinkerTinker优缺点QZone总结

Tinker优缺点

优点

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

缺点

  1. 与Qzone补丁技术一样,不支持即时生效,必须通过重启应用的方式才能生效(如果是软件启动后才发布的补丁,需要重启两次)。采用了ClassLoader机制
  2. 需要给应用开启新的进程才能进行合并,并且很容易因为内存消耗等原因合并失败。
  3. 合并时占用额外磁盘空间,对于多DEX的应用来说,如果修改了多个DEX文件,就需要下发多个patch.dex与对应的classes.dex进行合并操作时这种情况会更严重,因此合并过程的失败率也会更高。
  4. Tinker的接入侵入性太高了,打包也麻烦费时间。

QZone

Qzone也是采用的类加载的方案,它修复后是单独放在一个.dex中,插入到dexElements数组的最前面,让虚拟机去加载修复完后的方法。

腾讯热更新Tinker的故事前言TinkerTinker优缺点QZone总结

这样的热修复有一个大问题就是CLASS_ISPREVERIFIED

当两个调用关系的类不在同一个DEX时,就会产生异常报错。在APK安装时,虚拟机需要将classes.dex优化成odex文件,然后才会执行。

在这个过程中,会进行类的verify操作,如果调用关系的类都在同一个DEX中的话就会被打上CLASS_ISPREVERIFIED的标志,然后才会写入odex文件。

修复的步骤:

  1. 可以看出是通过获取到当前应用的Classloader,即为BaseDexClassloader
  2. 通过反射获取到他的DexPathList属性对象pathList
  3. 通过反射调用pathList的dexElements方法把patch.dex转化为Element[]
  4. 两个Element[]进行合并,把patch.dex放到最前面去
  5. 加载Element[],达到修复目的

总结

QZone与Tinker的类加载有点区别, QZone不用patch.dex与旧的.dex合并,所以会出现CLASS_ISPREVERIFIED问题。而Tinker的patch.dex与旧的.dex合并后在同一个.dex中,其实是将patch.dex的内容顺序排在了修复.dex的前面,能优先加载.patch里面的类。

相关链接:

  1. 热更新你都知道哪些?
  2. 热更新Sophix的爬坑之路
  3. 腾讯热更新Tinker的故事
  4. 阿里热更新Sophix的故事

扩展链接:

  1. ART与Dalvik、JVM之间的关系你懂了吗?
  2. 类加载机制原理解析

博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !