浅探热修复技术

来源:互联网 发布:2015网络十大神曲 编辑:程序博客网 时间:2024/06/03 13:18

热修复是一个为了即时解决紧急bug的技术。
从传统的开发流程看 修复一个bug 的流程:
重新发布版本 ——> 用户下载安装 —–> bug 修复
此流程 存在 bug修复的不及时、且用户下载安装成本较高 等缺陷。

热更新修复一个bug 的流程:
检测服务端是否存在 patch —存在—>下载 patch 到本地
|
|
|
加载本地patch
|
|
修复完成
由此可以看出 热修复 去修复一个bug 较为灵活,具有:无需重新发版,用户无感知修复,实时高效热修复 等优势,(热修复 更新的内容绕过了 应用市场的检测,具有一定的风险)
目前市面可供选择的热修复方案较多:HotFix、AndFix、sopHix,还有微信与美团推出的各自的热修复方案, 网上也有较多文章介绍各个热修复方案的优缺点, 在此是将网上的方案进行总结一下,做个备份。
市面上热修复方案虽然较多,但其内部实现依赖的是这两种技术:

1、art_method 替换技术2、class 类加载技术

采用 art_method 替换技术的方案,具有以下优点:

1、可以即时修复 bug方法,无需冷启动应用2、因为patch是以 method 为单位 生成的,所以补丁包较小3、对应用无侵入,几乎无性能损耗

但其缺点也较为显著:

1、不支持新增字段,以及修改<init>方法,也不支持对资源的替换。2、如果厂商修改了art,有可能导致 修复失效。

市面上使用art_method 替换技术的方案为: AndFix, 阿里百川的热修复方案与 SopHix
采用class 类加载技术 方案的优点为:

1、可以实现类的替换,兼容性高。2、可以修复资源与so库

不足:

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

市面上使用 类加载技术的方案较多,且各自都针对与 类加载的方案进行了不同的优化:
微信Tinker、HotFix 与 SopHix 应该都用到此方案。

在此将通过对比 HotFix、AndFix、SopHix,来看起优缺点:

方案对比 Andfix开源版本 HotFix 阿里最新版(Sophix) 方法替换 支持,除部分情况[0] 支持 支持 方法增加减少 不支持 支持 以冷启动方式支持[1] 方法反射调用 只支持静态方法 支持 以冷启动方式支持 即时生效 支持 不支持 视情况支持[2] 多DEX 不支持 … 支持 资源更新 不支持 … 支持 so库更新 不支持 … 支持 Android版本 支持2.3~7.0 全部支持 全部支持包含7.0以上 已有机型 大部分支持[3] 支持 全部支持 安全机制 无 加密传输及签名校验 加密传输及签名校验 性能损耗 低,几乎无损耗 高 低,仅冷启动情况下有些损耗 生成补丁 繁琐,命令行操作 繁琐,命令行操作 便捷,图形化界面 补丁大小 不大,仅变动的类 较大 不大,仅变动的资源和代码[3] 服务端支持 无 无 支持服务端控制[4] 是否开源 开源 开源 不开源

[0] 部分情况指的是构造方法、参数数目大于8或者参数包括long,double,float基本类型的方法。
[1] 冷启动方式,指的是需要重启app在下次启动时才能生效。
[2] 对于Andfix能够支持的代码变动情况,都能做到即时生效。而对于Andfix不支持的代码变动情况,会走冷启动方式,此时就无法做到即时生效。
[3] 由于支持了资源和库,如果有这些方面的更新,就会导致的补丁变大一些,这个是很正常的。并且由于只包含差异的部分,所以补丁已经是最大程度的小了。
[4] 提供服务端的补丁发布和停发、版本控制和灰度功能,存储开发者上传的补丁包。

AndFix
开源:https://github.com/alibaba/AndFix
AndFix 是阿里最早出的 一个热更新的框架,art_methods 的修复技术就是由此而来的,他的是在native 层实现方法的修复的,他的流程大概为:
1、从服务端获取patch 包
2、 解析patch 包,获取new方法,与要替换的类,并以此获取old 方法。
3、在native 层,使用jni 的 FromReflectedMethod 方法,获取old 、new 在内存中的方法指针
4、将new 、old 方法指针 中的数据对掉。
由此实现了方法替换修复。
他的关键方法为:

   void replace_6_0(JNIEnv* env, jobject src, jobject dest) {//通过Method对象得到底层Java函数对应ArtMethod的真实地址。art::mirror::ArtMethod* smeth =        (art::mirror::ArtMethod*) env->FromReflectedMethod(src);art::mirror::ArtMethod* dmeth =        (art::mirror::ArtMethod*) env->FromReflectedMethod(dest);... ...//把旧函数的所有成员变量都替换为新函数的。smeth->declaring_class_ = dmeth->declaring_class_;smeth->dex_cache_resolved_methods_ = dmeth->dex_cache_resolved_methods_;smeth->dex_cache_resolved_types_ = dmeth->dex_cache_resolved_types_;smeth->access_flags_ = dmeth->access_flags_;smeth->dex_code_item_offset_ = dmeth->dex_code_item_offset_;smeth->dex_method_index_ = dmeth->dex_method_index_;smeth->method_index_ = dmeth->method_index_;smeth->ptr_sized_fields_.entry_point_from_interpreter_ =dmeth->ptr_sized_fields_.entry_point_from_interpreter_;smeth->ptr_sized_fields_.entry_point_from_jni_ =dmeth->ptr_sized_fields_.entry_point_from_jni_;smeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ =dmeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;}

由此可知 AndFix 是在methods 层级进行修复的,可以即时修复,且对apk性能影响较小,因为其开源,可控性较高,且针对业务需要,可以自己进行定制。但其缺陷也较为明显,如果厂商对art 进行的修改,那么此 热修复框架,不会生效, 无法进行 资源与so的修复,且缺少 服务端控制,需要自己搭建服务端, 因为 此方法的局限性 , 仅能进行方法层级修复,缺少patch 的安全验证,无法添加新的方法与字段,缺陷较多

HotFix
开源:http://www.jianshu.com/p/6f0ae1e364d9
他使用的class 类加载方案, 此方案依赖的 dex 分包技术,他的流程为:
1、下载云端的patch 包
2、冷启动应用
3、在打开引用时,加载本地 patch 包,解析出 patch 包中 dex 文件
4、将dex 文件加载到内存中
到此修复完成, patch 包中的 dex 文件,包含了需要修复的类与文件,因为 如果应用存在多个dex包,系统在调用方法时,会遍历dex 文件,查找到对应类,然后去调用方法,
class 类加载方案是 将 需要修复的 类抢先加载到,ClassLoader 中的dex 数组中,遍历时 会先遍历到修复后的类,并将其返回,以此达到修复的目的。 其核心是依赖系统的此方法:

/* package */final class DexPathList {...public Class findClass(String name, List<Throwable> suppressed) {        //遍历该数组    for (Element element : dexElements) {        //初始化DexFile        DexFile dex = element.dexFile;        if (dex != null) {            //调用DexFile类的loadClassBinaryName方法返回Class实例            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);            if (clazz != null) {                return clazz;            }        }    }           return null;}...} 

用class 类加载技术,是类层级上的 修复技术,可以新加字段,新加方法,且也是开源的,可控性也较高,可以针对需求 自己定制,例如微信的Tinker 方案就是 此技术结合差分包技术实现的,但此方案的缺陷也 较为明显, 修复的类,会在内存中存在2份 ,如果修复的类较多,那么对内存有影响, 且因为是以类为单位制作patch的,patch 较大, 需要冷启动,且也缺少服务端,需要自己实现。

Sophix
Sophix 是 阿里2017年6月份推出的一款商业化的 热更新修复方案, 他是从AndFix后的第三次改进更新(第二次为阿里百川的方案),虽然不开源,但从其介绍来看 应该是在 art_method 替换技术上进行升级并结合 class 加载技术来实现的,在生成patch时 判断使用那种方案, 在apk 加载patch包时,解析patch包,根据当前patch 包的方案,判断是否 需要冷启动。
SopHix 对art_method 替换技术 进行的升级,不再进行old、new 方法指针的繁琐的替换操作了,直接进行 memcpy操作,将之前一大堆的替换操作凝缩为了:

memcpy(smeth, dmeth, sizeof(ArtMethod));

而获取 ArtMethod 的大小sizeof(ArtMethod),是通过自己写两个连续的方法,通过方法指针的差值来实现的。
改进后的 art_method 的替换,即使厂商修改art 后也不会有影响,依然会替换成功。

且较为称道的是,他存在完善的后台服务端,可以对指定patch 包进行灰度测试,或直接发布。
但可惜的是不开源啊。