项目发版上线后,发现一个bug,这种时候,要么忍受线上bug的存在,等待下个大版本更新时一并解决,要么发布小版本更新,但是更新小版本这种事情对于很多用户来讲,是一个体验很不好的事情:1、浪费用户流量,虽然现在流量不怎么值钱,但是下载安装的等待过程也是很影响用户体验;2、对于更新狂人来讲,这些用户热爱更新,但是更新完毕后仅仅是修复一个小bug,对于没碰到这个bug的用户来讲,显得有些莫名其妙;3、对于更新恐惧症的用户来讲,这部分人不喜欢更新应用,频发的弹窗红点等提醒容易招致反感;
基于以上问题的存在,我们决定引入热更新方案,热更新方案的解决思路是:代码出现bug时,我们会在出现bug的部位打补丁,这样在应用执行到出现bug的位置时,会走补丁内的逻辑,从而绕开有bug的代码。
虽然 Google play store 明令禁止使用热更新(因为热更新可以绕开应用审核机制),但是国内形势下的热更新依然火热,这也从侧面证明了热更新存在的必要性。
受限于Android应用的编译与安装机制,目前的热更新方案总会有或多或少的不足之处,但是我们可以根据自己的项目需求,选择一款适合自己应用的热更新方案,以下为市场上现存的主流热更新方案思路:
(一)、QZone超级补丁:
超级补丁基于DEX分包方案,核心思路是:把bug代码修复后,放到一个单独的dex中,然后在应用启动时优先加载修改后的dex文件,由于java虚拟机的类加载的双亲委派机制,同一个类只会加载一次,这样在应用执行到存在bug的类时,会直接执行事先加载好的dex文件,从而事先绕开bug的目的
优点:可以直接实现类文件的替换,修复范围较大
缺点:1、由于应用启动时dex仅加载一次,因此修复bug必须通过重启应用才能生效;2、在应用启动时实时替换dex文件加载位置,当修复bug所设计的类文件较多时,会引发应用启动耗时的问题;3、侵入性太大,apk在安装时会进行odex化,此时在同一个dex文件中的class都会被打上CLASS_ISPREVERIFIED标志,而热更新的类却是存在于单独的一个dex中,在执行时会引发检测报错,为了解决这个问题,AZone方案在每一个类文件的构造方法中都单独引入了一个AntilazyLoad.class,会对每一个class文件作出改动
(二)、Tinker热修复方案:
针对QZone方案的侵入性太大,Tinker在QZone的基础上提出了一个dex差量包,对dex文件进行整体替换,Tinker的核心思路与QZone类似,区别之处在于Tinker不再对补丁dex文件执行提前插入操作,而是以下发差量补丁的形式下发patch.dex,接着与应用的class.dex文件合并,然后整体替换掉应用的dex文件,从而实现热修复的目的
优点:1、合成整包,不再针对每个class类插入AntilazyLoad.class,侵入像大大降低;2、性能提高,由于不需要调整class文件的加载顺序,兼容性和稳定性都相对于QZone提高不少;3、支持非export类型的Activity(采用提前占位的形式)
缺点:1、与QZone一样,补丁的生效需要依赖应用的重启;2、合成dex的过程性能消耗较大,容易因为内存消耗的原因导致dex合成失败;3、对于多dex应用来讲,如果修改了多个dex文件,那么就需要进行多个dex的合成,失败率再次提高
(三)、Robust热修复方案:
Robust是美团开发团队推出的技术方案,核心思路是采用代码插桩的方式,对每个method方法进行修改,具体步骤如下:1、对每个class文件在编译阶段手动插入ChangeQuickRedirect属性;2、对每个method方法进行修改,当方法执行时,会首先判断ChangeQuickRedirect知否为null,如果不为null,则执行插桩后的代码,否则执行代码原有逻辑;3、当客户端检测到存在patch.dex补丁时,会调用DexClassLoader加载patch.dex文件,通过反射拿到PatchesInfoImpl.class类进行初始化,进而获取到要patch的class类,然后通过反射拿到当前运行环境中的class类的实例,讲其中的changeQuickRedirect属性赋值为patch.dex中的新对象,从而执行新对象的方法,实现修复bug的目的
优点:1、即时生效,无需等待应用重启;2、Robust只是在正常的使用DexClassLoader,只要java虚拟机的类加载机制没有发生变化,那么几乎不存在兼容性问题;3、不修改java的执行逻辑,没有额外骚操作,稳定性极高
缺点:1、侵入性极大,针对每个method进行插桩修改;2、仅支持新增类,不支持类文件的替换,不支持现有类中的属性增减等操作
(四)、Andfix热修复方案:
Andfix是阿里百川推出的热修复方案;从定位上来讲,QZone和Tinker属于功能级修复,依赖于应用重启才能实现修复;而Andfix和Robust则定位于紧急bug的修复工作,下发补丁后即时生效,无需重启应用。其核心思路是通过Native层修改Filed指针的形式达到方法的实时替换,从而实现无需重启即可立即生效的目的。主要步骤如下:1、在运行时装载libdvm.so动态库,通过Native层获取到需要被替换方法的类对象;2、得到新旧方法的指针;3、操纵指针,指向修复后的新方法
优点:1、即时修复,无需重启;2、采用差量技术,生成的补丁包较小;3、无侵入性,由于是执行指针转换,对应用性能几乎无影响
缺点:Andfix执行的指针替换实际上就是ArtMethod的修改替换,ArtMethod记录了方法执行时所包括的所属类和内存地址等信息,Andfix正是通过篡改ArtMethod,将补丁方法ArtMethod的成员值逐一赋给旧方法,实现替换,然而,Andfix的ArtMethod方法结构是根据Android开源代码写死的,而国内定制ROM的各种魔改,经常会导致ArtMethod结构发生变化,从而导致Andfix修复方案的兼容性较低
(五)、Sophix热修复方案:
Sophix是阿里推出的闭源收费热修复方案,它是充分总结吸收了市面上各种热修复方案的优缺点,可以说是集众加之所长的一款热修复方案,支持即时生效和重启生效两种修复策略,其核心思路如下:1、即时生效:Sophix的即时生效方案同样是采用Native底层替换实现,不同之处在于Sophix采用了对旧ArtMethod进行完整替换,通过动态测量ArtMethod的size(通过c层的mempy(dest ,src ,size)方法),进行全量拷贝。这样做无论ArtMethod如何修改,只需要统一执行拷贝,就可以完成替换,完全无视修改虚拟机导致的ArtMethod结构差异;2、重启生效:作为Sophix兜底方案,Sophix的重启修复修复机制与Tinker类似,但是对dex对比的精细力度是以class为单位的,直接利用Android原生的类查找和合成机制,既避免的内存占用过大的问题,也不破坏dex的结构,这样既降低了合成补丁带来的开销,也提高了合成的成功率
优点:集中家之所长,兼容即时生效与重启生效两种修复策略
缺点:1、每个应用只有5万台免费额度,超过后需要付费;2、不开源,只能基于官方文档进行集成,自由度较小
热修复方案需要对java虚拟机的运行原理有一定的了解,同时也要求熟悉Android的编译和运行原理原理,掌握热修复方案的核心思想,对于个人的技能水平有不少的提升,通过对于项目的稳健运行也有相当大的帮助,作为开发人员,我们要知其然并知其所以然,才能更好提升自己
1、四大热修复方案对比分析
2、QQ空间补丁方案解析
3、Android中热修复框架Robust原理解析
4、阿里热更新框架 Sophix