Android热修复技术选择和原理分析

| Tinker | 15.2k | 29 days ago | 1.9.14.7 |

| Robust | 3.7k | 4 months ago | 0.4.99 |

| Aceso | 791 | 3 years ago | 0.0.3 |

| Amigo | 1.3k | 3 years ago | 0.6.* |

| RicooFix | 1.6k | 4 years ago | 无 |

可以看到,近期还在更新的有Tinker和Robust,其他的都是至少三年之前的更新。

如何选择热修复框架

三个方面进行考虑

1.项目需求

方法级别修复,资源修复,so库的修复

对平台兼容性要求和成功率要求

有需求对分发进行控制,对监控数据进行统计,补丁包进行管理

是否付费

2.学习,使用成本

学习成本

代码侵入性

调试维护成本

3.技术保障,稳定性

比如GitHub Star,大公司技术保障,专人维护

热度高,社区活跃

小结

从这三个方面考虑,最后筛选出三个比较优秀的热修复库,Sophix,Tinker,Robust

如果考虑付费,Sophix和Tinker付费版(云服务),我支持Sophix,性能消耗低,支持即时生效,对代码无侵入,免费阈值的支持更好。

如果不考虑付费,只需要支持方法级别的Bug修复,不支持资源以及so库,推荐使用Robust,否则使用Tinker免费版。

当然如果公司实力够牛逼,可以考虑自研,灵活性以及可控性最强。

代码、资源、so库修复

AndroidManifest出现Bug是无法修复的,因为它是由系统进行解析的,系统会直接获取安装包里唯一的AndroidMainfest.xml文件,在解析过程不会访问补丁包信息。

代码修复:任何的热修复方案,想要改变代码逻辑,都需要在补丁包里包含一个新逻辑的dex文件。

资源修复:有些资源,比如桌面图标,通知栏图标以及RemoteView之类的资源,是由系统直接解析安装包里的资源得到的,因此对于这类资源,任何热修复方案都无法进行资源替换和修复。

so库修复:so库的修复思路应该是最明确的。在Android系统中,所有的so库都是由System.load进行加载的,因此只要找到办法在加载的时候优先加载补丁包的so库,而不是加载原有安装包的so库,就能够进行完整的底层代码替换了。

代码修复

代码修复主要有三大主要方案,阿里系的底层替换和腾讯系的类加载方案以及美团的javaHook方案(Instant Run原理)。

  1. 底层替换方案限制颇多,但是时效性最好,加载轻快,立即见效。

传统的底层替换方案(Dexposed,AndFix),依赖直接修改虚拟机方法实体的具体字段实现的。不同厂商/版本对ArtMethod结构体的结构修改带来的问题。每个Java方法在art中都对应着一个ArtMethod,ArtMethod记录了这个Java方法的所有信息,包括所属类,访问权限,代码执行地址等等。

实现一种不修改底层具体结构的替换方式,解决兼容性问题,代码量大大减少。native层面替换,把ArtMethod方法作为整体进行替换。

  1. 类加载方案时效性差,需要重新类启动才能见效,但修复范围广,限制少。

类加载方案的原理是在App重新启动后让Classloader去加载新的类。

QQ空间方案会入侵打包流程,并且为hack添加一些无用的信息,不优雅。

QFix方案需要获取底层虚拟机的函数,稳定性不够,无法新增public函数

Tinker方案是完整的全量的dex文件加载,将补丁合成的方案做到了极致,从dex的方法和指令维度进行全量合成,对于dex内容的比较粒度过细,实现较为复杂,性能消耗比较严重,时空转换性价比不高。

dex比较的最佳粒度,应该是类的维度,采用全量合成dex的技术,这个技术方案是从手机淘宝插件化框架Atlas汲取的。直接利用Android原有的类查找与合成机制,快速合成新的的全量dex文件。这样既不需要处理合成时的方法数超过原有方法数的情况,也不会对dex的结构进行破坏性重构。

​ 我们重新编排了包中dex文件的顺序。这样虚拟机在查找类的时候,会优先找到classes.dex中的类,然后才是classes2.dex,classes3.dex,也可以看作是dex文件级别的类插桩方案。这个方式十分巧妙,它对旧包与补丁包中的classes.dex顺序进行了打破和重组,最终使系统可以自然的识别到这个顺序,以实现类覆盖的目的,大大减少合成补丁的开销。

Sophix使用了两者的结合,自动选择,小修改,在底层替换方案限制范围内的,直接采用底层替换热修复,可以做到及时生效。其他采用类加载替换方案。

  1. Robust的JavaHook方案的原理与Instant Run的代码插桩原理一致,优点是实时生效,不需要重新启动,高兼容性(Robust只是在正常的使用DexClassLoader),高稳定性,修复成功率高达99%。支持方法级别的修复,包括静态方法。支持增加方法和类。支持ProGuard的混淆,内联优化等操作。

缺点是代码是侵入式的,会在原有的类中加入相关代码,so库和资源的替换暂时不支持。会增大apk的体积,平均一个函数会比原来增加17.47个字节,10万个函数会增加1.67M。

Sophix方案

热部署(及时生效)

在native层面替换,把ArtMethod方法作为整体进行替换,实时生效,但同时也会带来诸多限制:

访问权限的问题

  1. 方法调用时的权限检查

  2. 同名包下的权限问题(设置新类的ClassLoader为原来类就可以了,通过反射进行设置)

  3. 反射调用非静态方法产生的问题

及时生效带来的限制

以下两种情况是不适用的:

  1. 引起了原有的类中发生结构变化的修改(字段,方法增加减少),改成以冷启动支持

  2. 修复了的非静态方法会被反射调用,改成以冷启动方式支持

编译期和语言特性的影响

1. 内部类编译

  1. 静态内部类/非静态内部类的区别

  2. 内部类和外部类互相访问

外/内部类为了访问内/外部类私有的域/方法,编译器会自动为内部类生成access$数字编号相关方法,JVM规范,好像是提供了一个静态方法

  1. 热部署解决方案

一个外部类如果有内部类,把所有的method/field的私有访问权限改成protected或public或者默认访问权限。

同时把内部类所有的method/field的私有访问权限改成protected或public或者默认访问权限。

2. 匿名内部类编译

  1. 匿名内部类编译命名规则(access$**)

  2. 热部署解决方案

应该极力避免插入一个新的匿名内部类。当然如果匿名内部类是插入到外部类的末尾࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值