Android热修复技术选型——三大流派解析

2015年以来,Android开发领域里对热修复技术的讨论和分享愈来愈多,同时也出现了一些不一样的解决方案,如QQ空间补丁方案、阿里AndFix以 及微信Tinker,它们在原理各有不一样,适用场景各异,到底采用哪一种方案,是开发者比较头疼的问题。本文但愿经过介绍QQ空间补丁、Tinker以及基于AndFix的阿里百川HotFix技术的原理分析和横向比较,帮助开发者更深刻了解热修复方案。数组

技术背景
——————————————————————————————————————————————————————————————————————
1、正常开发流程
图片描述
从流程来看,传统的开发流程存在不少弊端:
• 从新发布版本代价太大
• 用户下载安装成本过高
• BUG修复不及时,用户体验太差微信

2、热修复开发流程
图片描述
而热修复的开发流程显得更加灵活,优点不少:函数

• 无需从新发版,实时高效热修复
• 用户无感知修复,无需下载新的应用,代价小
• 修复成功率高,把损失降到最低性能

业界热门的热修复技术
——————————————————————————————————————————————————————————————————————————————
热修复做为当下热门的技术,在业界内比较著名的有阿里巴巴的AndFix、Dexposed,腾讯QQ空间的超级补丁和微信的Tinker。最近阿里百川推出的HotFix热修复服务就基于AndFix技术,定位于线上紧急BUG的即时修复,因此AndFix技术这块咱们重点分析阿里百川HotFix。下面,咱们就分别介绍QQ空间超级热补丁技术和微信Tinker以及阿里百川的HotFix技术。 测试

1、QQ空间超级补丁技术
超级补丁技术基于DEX分包方案,使用了多DEX加载的原理,大体的过程就是:把BUG方法修复之后,放到一个单独的DEX里,插入到dexElements数组的最前面,让虚拟机去加载修复完后的方法。
图片描述
当patch.dex中包含Test.class时就会优先加载,在后续的DEX中遇到Test.class的话就会直接返回而不去加载,这样就达到了修复的目的。优化

可是有一个问题是,当两个调用关系的类不在同一个DEX时,就会产生异常报错。咱们知道,在APK安装时,虚拟机须要将classes.dex优化成odex文件,而后才会执行。在这个过程当中,会进行类的verify操做,若是调用关系的类都在同一个DEX中的话就会被打上CLASS_ISPREVERIFIED的标志,而后才会写入odex文件。ui

因此,为了能够正常地进行打补丁修复,必须避免类被打上CLASS_ISPREVERIFIED标志,具体的作法就是单独放一个类在另外DEX中,让其余类调用。google

咱们来逆向手机QQ空间APK看一下具体的实现:spa

先进入程序入口QZoneRealApplication,在attachBaseContext中进行了两步操做:修复CLASS_ISPREVERIFIED标志致使的unexpected DEX problem异常、加载修复的DEX。插件

图片描述

  1. 修复Unexpected DEX Problem异常
    先看代码,

图片描述
能够看到,这里是要加载一个libs目录下的dalvikhack.jar。在项目的assets/libs找到该文件,解压获得’classes.dex’文件,逆向打开该DEX文件,
图片描述

经过不一样的DEX加载进来,而后在每个类的构造方法中引用其余DEX中的惟一类AnitLazyLoad,避免类被打上CLASS_ISPREVERIFIED标志。
图片描述
在无修复的状况下,将DO_VERIFY_CLASSES设置为false,以提升性能。只有在须要修复的时候,才设置为true。
图片描述

至于如何加载进来,与下面第二个步骤基本相同。

  1. 加载修复的DEX
    从loadPatchDex()方法进入,通过几回跳转,到达核心的代码段,SystemClassLoaderInjector.c()。因为进行了混淆和屡次方法的跳转,因而将核心代码段作了以下整理:

图片描述
修复的步骤为:

  1. 能够看出是经过获取到当前应用的Classloader,即为BaseDexClassloader

  2. 经过反射获取到他的DexPathList属性对象pathList

  3. 经过反射调用pathList的dexElements方法把patch.dex转化为Element[]

  4. 两个Element[]进行合并,把patch.dex放到最前面去

  5. 加载Element[],达到修复目的

总体的流程图以下:
图片描述
从流程图来看,能够很明显的找到这种方式的特色:
优点:

  1. 没有合成整包(和微信Tinker比起来),产物比较小,比较灵活

  2. 能够实现类替换,兼容性高。(某些三星手机不起做用)
    不足:

  3. 不支持即时生效,必须经过重启才能生效。

  4. 为了实现修复这个过程,必须在应用中加入两个dex!dalvikhack.dex中只有一个类,对性能影响不大,可是对于patch.dex来讲,修复的类到了必定数量,就须要花很多的时间加载。对手淘这种航母级应用来讲,启动耗时增长2s以上是不可以接受的事。

  5. 在ART模式下,若是类修改告终构,就会出现内存错乱的问题。为了解决这个问题,就必须把全部相关的调用类、父类子类等等所有加载到patch.dex中,致使补丁包异常的大,进一步增长应用启动加载的时候,耗时更加严重。

2、微信Tinker
微信针对QQ空间超级补丁技术的不足提出了一个提供DEX差量包,总体替换DEX的方案。主要的原理是与QQ空间超级补丁技术基本相同,区别在于再也不将patch.dex增长到elements数组中,而是差量的方式给出patch.dex,而后将patch.dex与应用的classes.dex合并,而后总体替换掉旧的DEX文件,以达到修复的目的。
图片描述
咱们来逆向微信的APK看一下具体的实现:
先找到应用入口TinkerApplication,在onBaseContextAttached()调用了loadTinker(),
图片描述
进入TinkerLoader的tryLoad()方法中,
图片描述
从方法名能够预见,在tryLoadPatchFilesInternal()中尝试加载本地的补丁,再通过跳转进入核心修复功能类SystemClassLoaderAdder.class中。
图片描述
代码中能够看出,根据Android版本的不一样,分别采起具体的修复操做,不过原理都是同样的。咱们以V19为例,
图片描述
从代码中能够看到,经过反射操做获得PathClassLoader的DexPatchList,反射调用patchlist的makeDexElements()方法吧本地的dex文件直接替换到Element[]数组中去,达到修复的目的。

对于如何进行patch.dex与classes.dex的合并操做,这里微信开启了一个新的进程,开启新进程的服务TinkerPatchService进行合并。
图片描述
总体的流程以下:
图片描述
从流程图来看,一样能够很明显的找到这种方式的特色:
优点:

  1. 合成整包,不用在构造函数插入代码,防止verify,verify和opt在编译期间就已经完成,不会在运行期间进行。

  2. 性能提升。兼容性和稳定性比较高。

  3. 开发者透明,不须要对包进行额外处理。
    不足:

  4. 与超级补丁技术同样,不支持即时生效,必须经过重启应用的方式才能生效。

  5. 须要给应用开启新的进程才能进行合并,而且很容易由于内存消耗等缘由合并失败。

  6. 合并时占用额外磁盘空间,对于多DEX的应用来讲,若是修改了多个DEX文件,就须要下发多个patch.dex与对应的classes.dex进行合并操做时这种状况会更严重,所以合并过程的失败率也会更高。

3、阿里百川HotFix
阿里百川推出的热修复HotFix服务,相对于QQ空间超级补丁技术和微信Tinker来讲,定位于紧急BUG修复的场景下,可以最及时的修复BUG,下拉补丁当即生效无需等待。
图片描述
一、AndFix实现原理
AndFix不一样于QQ空间超级补丁技术和微信Tinker经过增长或替换整个DEX的方案,提供了一种运行时在Native修改Filed指针的方式,实现方法的替换,达到即时生效无需重启,对应用无性能消耗的目的。
原理图以下:
图片描述
二、AndFix实现过程

对于实现方法的替换,须要在Native层操做,通过三个步骤:
图片描述
接下来以Dalvik设备为例,来分析具体的实现过程:

一、setup()
图片描述

对于Dalvik来讲,遵循JIT即时编译机制,须要在运行时装载libdvm.so动态库,获取如下内部函数:
1) dvmThreadSelf( ):查询当前的线程;
2)dvmDecodeIndirectRef( ):根据当前线程得到ClassObject对象。

二、setFieldFlag
图片描述
该操做的目的:把 private、protected的方法和字段都改成public,这样才可被动态库看见并识别,由于动态库会忽略非public属性的字段和方法。

三、replaceMethod
图片描述

该步骤是方法替换的核心,替换的流程以下:

图片描述
AndFix对ART设备一样支持,具体的过程与Dalvik类似,这里再也不赘述。

从技术原理,不难看出阿里百川HotFix的几个特色:

优点:

  1. BUG修复的即时性

  2. 补丁包一样采用差量技术,生成的PATCH体积小

  3. 对应用无侵入,几乎无性能损耗
    不足:

  4. 不支持新增字段,以及修改<init>方法,也不支持对资源的替换。

  5. 因为厂商的自定义ROM,对少数机型暂不支持。

综合分析以下:

图片描述

热修复技术的坑与解
——————————————————————————————————————————————————————————————————————
咱们能够看到,QQ空间超级补丁技术和微信Tinker的修复原理都基于类加载,在功能上已经支持类、资源的替换和新增,功能很是强大。既然已经有了这么强大的热修复技术,为何阿里百川还要推出本身的热修复方案HotFix呢?

1、多DEX带来的性能影响
咱们知道,多DEX方案原来是用于解决应用方法数65k的问题,如今google也官方支持了MultiDex的实现方案。超级补丁技术和Tinker却做为一种热修复的方案,生平给应用增长了多个DEX,而多DEX技术最大的问题在于性能上的坑,所以基于这种方案的补丁技术影响应用的性能是无疑的。

  1. 启动加载时间过长
    咱们能够看到,超级补丁技术和Tinker都选择在Application的attachBaseContext()进行补丁dex的加载,即时这是加载dex的最佳时机,可是依然会带来很大的性能问题,首当其冲的就是启动时间太长。

对于补丁DEX来讲,应用启动时虚拟机会进行dexopt操做,将patch.dex文件转换成odex文件,这个过程自己很是耗时。而这个过程又要求在主线程中,以同步的方式执行,不然没法成功进行修复。就DEX的加载时间,大概作了如下的时间测试。
图片描述
经过上表能够看到,随着patch.dex的尺寸增长,在不作任何优化的状况下,启动时间也直线增加。对于一个应用来讲,这简直是灾难性的。

  1. 易形成应用的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,就更加的得不偿失。

总的来讲,热修复本质的目的是为了保证应用更加稳定,而不是为了更强大的功能引入更大的风险和不稳定性。

2、 热修复 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的更为合适,它更加轻量,能够在不重启的状况下生效,且对性能几乎没有影响。

因此阿里百川针对修复紧急BUG的场景,推出了HotFix这项在线热修复的轻服务。尽心提供最快捷的修复,让用户作到真正无感知,即时生效。 相比较微信Tinker、QQ空间超级补丁技术更多地把场景定位在发布新功能上,采用ClassLoader的模式,以牺牲少许性能为代价去实现类、资源新增或替换的功能,阿里百川HotFix对应用自己作到无侵入,几乎无性能损耗。

图片描述

相关文章
相关标签/搜索