I - android 热修复方案及选择
一.概念
热修复说白了就是”即时无感打补丁”,
无需重新发版,实时高效热修复;用户无感知修复,无需下载新的应用,代价小;
修复成功率高,把损失降到最低。
二.现有方案
Dex的热修复目前来看基本上有四种方案:
- 阿里系的从native层入手,见AndFix
- QQ空间的方案,插桩,见安卓App热补丁动态修复技术介绍
- 微信的方案,见微信Android热补丁实践演进之路,dexDiff和dexPatch,方法很牛逼,需要全量插入,但是这个全量插入的dex中需要删除一些过早加载的类,不然同样会报class is pre verified异常,还有一个缺点就是合成占内存和内置存储空间。微信读书的方式和微信类似,见 Android Patch 方案与持续交付,不过微信读书是miniloader方式,启动时容易ANR,在我锤子手机上变现出来特别明显,长时间的卡图标现象。
- 美团的方案,也就是instant run的方案,见Android热更新方案Robust
对比:
单就热修复线上 APP 某一处或多处 bug 来说,Andfix能做到即时修复,且操作简单,不用生成较多的 patch.dex 包,能轻松解决紧急问题。
但对于如非紧急 bug 的修复及小版本的发布,对即时生效性要求不高的情况,Tinker 支持的替换内容较丰富,更胜一筹。
阿里将 Andfix 升级为商业版 SDK Sophix;腾讯将 Tinker 升级为 Bugly。
Sophix 不但支持即时修复,还支持再次启动修复类、so 文件、资源等。作为商用 APP 集成 Sophix 是很好的选择。
但 Tinker 的开源,为其带来了大量的使用者和测试者,除此以外还与各大手机厂商建立联系,使得各厂商在系统定制时也会考虑是否影响热修复的问题。所以 Tinker 的兼容性可见一斑。
三:热修复技术的坑与解
一、多DEX带来的性能影响
我们知道,多DEX方案原来是用于解决应用方法数65k的问题,现在google也官方支持了MultiDex的实现方案。超级补丁技术和Tinker却作为一种热修复的方案,平生给应用增加了多个DEX,而多DEX技术最大的问题在于性能上的坑,因此基于这种方案的补丁技术影响应用的性能是无疑的。
1. 启动加载时间过长
我们可以看到,超级补丁技术和Tinker都选择在Application的attachBaseContext()进行补丁dex的加载,即时这是加载dex的最佳时机,但是依然会带来很大的性能问题,首当其冲的就是启动时间太长。
对于补丁DEX来说,应用启动时虚拟机会进行dexopt操作,将patch.dex文件转换成odex文件,这个过程本身非常耗时。而这个过程又要求在主线程中,以同步的方式执行,否则无法成功进行修复。就DEX的加载时间,大概做了以下的时间测试。
通过上表可以看到,随着patch.dex的尺寸增加,在不做任何优化的情况下,启动时间也直线增长。对于一个应用来说,这简直是灾难性的。
2. 易造成应用的ANR和Crash
由于多DEX加载导致了启动时间变长,这样更容易引发应用的ANR。我们知道当应用在主线程等待超过5s以后,就会直接导致长时间无响应而退出。超级补丁技术为保证ART不出现地址错乱问题,需要将所有关联的类全部加入到补丁中,而微信Tinker采取一种差量包合并加载的方式,都会使要加载的DEX体积变得很大。这也很大程度上容易导致ANR情况的出现。
除了应用ANR以外,多DEX模式也同样很容易导致Crash情况的出现。在ART设备中为了保证不出现地址错乱,需要把修改类的所有相关类全部加入到补丁中,这里会出现一个问题,为了保证补丁包的体积最小,能否保证引入全部的关联类而不引入无关的类呢?一旦没有引入关联的类,就会出现以下的异常:
- NoClassDefFoundError
- Could Not Find Class
- Could Not Find Method
出现这些异常,就会直接导致应用的Crash退出。
所以,不难看出如果我们需要修复一个不是Crash的BUG,但是因为未加入相关类而导致了更严重的Crash,就更加的得不偿失。
总的来说,热修复本质的目的是为了保证应用更加稳定,而不是为了更强大的功能引入更大的风险和不稳定性。
二、 热修复 or 插件化?
我们经常提到热修复和插件化,这都是当下热门的新兴技术。在讲述之前,需要对这两个概念进行一下解释。
- 热修复:当线上应用出现紧急BUG,为了避免重新发版,并且保证修复的及时性而进行的一项在线推送补丁的修复方案。
- 插件化:一个程序划分为不同的部分,以插件的形式加载到应用中去,本质上它使用的技术还是热修复技术,只是加入了更多工程实践,让它支持大规模的代码更新以及资源和SO包的更新。
显然,从概念上我们可以看到,插件化使用场景更多是功能上的,热修复强调微小的修复。从这个层面来说,插件化必然功能更加强大,能做的事情也更多。QQ空间超级补丁技术和微信Tinker从类、资源的替换和更新上来看,与其说是热修复,不如说是插件化技术的实践。
QQ空间超级补丁技术和微信Tinker提供了更加强大的功能,但是对应用的性能和稳定有较大的影响,就BUG修复的这个使用场景上还不够明确,并且显得过重。
针对应用的性能损耗,我们可以举例做一个对比:
某APP的启动载入时间为3s左右,本身就是基于多DEX模式的实现。
分别接入三种热修复服务,根据腾讯提供超级补丁技术和Tinker的数据,那么会变成以下的场景:
1. 阿里百川HotFix:启动时间几乎无增加,不增加运行期额外的磁盘消耗。
2. QQ空间超级补丁技术:如果应用有700个类,启动耗时增加超过2.5s,达到5.5s以上。
3. 微信Tinker:假设应用有5个DEX文件,分别修改了这5个DEX,产生5个patch.dex文件,就要进行5次的patch合并动作,假设每个补丁1M,那么就要多占用7.5M的磁盘空间。
显然对于修复紧急BUG这个场景,阿里百川HotFix的更为合适,它更加轻量,可以在不重启的情况下生效,且对性能几乎没有影响。