Android热修复主流方案

热修复方案按照是否必须重启分为两类:重启生效 / 即时生效。按照实现方式可以分为3类:Java层的实现 / Native层的实现 / Java Native混合实现

阿里AndFix 方案(已弃用)

AndFix 是 无需重启 的 Native层 的实现。但是,AndFix目前已经3年多没维护更新。
因为阿里已经有了新的替代方案,不再需要维护。另外,这种纯Native的实现方式,兼容性十分堪忧。
既然已经被弃用,但是稍微了解一下实现方案也是没问题的。

假设有一段程序按照A=>B=>C的顺序去执行,然而发现执行到B的时候,存在一个bug,AndFix的修复方式为:在Native层执行到B方法的时候,把方法的指针指向了补丁包里的B方法,绕过了原来的B方法。从而达到了修复bug的目的。

1
2
3
4
5
//注解表示补丁中此方法会替换apk中哪个方法
@MethodReplace(clazz="cn.appblog.utils", method="test")
public void test() {
...
}

在需要修复的方法上面,加上@MethodReplace注解,给定参数classmethod,表示该方法替换为哪个类的哪个方法。

缺陷:修复粒度为:method。AndFix只能以方法为粒度去修复bug。作用有限,针对大范围的类替换和资源替换,那就无能为力。

美团 Robust方案

Robust 是即时生效Java层实现的热修复实现方案.

美团的Robust方案,是参考了谷歌的InstantRun方案(之前在Android Studio里面有这个选项,可以选择打开Instant Run运行app)的思路而设计出来的。

其主要设计思想就是一句话:编译打包时,在程序的某些方法里面,都插入一段代码(全自动操作)

1
2
3
4
if (changeQuickRedirect != null) {
//
return 修复的实现
}

changeQuickRedirect不为空的时候,该方法就会命中if (changeQuickRedirect != null),从而执行修复的实现代码。当为空的时候,则正常执行原逻辑。

而平时我们自己编码,只需要加上一个@Modify注解,来标记这个方法需要打补丁包,也就是需要插入上面这个if (changeQuickRedirect != null)代码段。

1
2
3
4
5
//编写的代码
@Modify //改动代码后手动添加注解用于补丁包生成
public long getIndex() {
return 100;
}

(1)为什么Robust可以即时生效?

上图解析:利用ClassLoader,当客户端手机收到补丁包 patch.dex的时候,执行补丁包,把指定方法的changeQuickRedirect用反射的手段赋值,让它变成非空。从而让下一次程序逻辑走到这里的时候,走修复之后的逻辑。

(2)Robust是如何将这么多if (changeQuickRedirect != null)代码段插入到代码逻辑中的?

上图中的if (changeQuickRedirect != null)代码段,并非我们手动编写,而是由Robust框架自动插入的
这个技术叫做字节码插桩,意为:对class进行操作,按照class文件的格式,插入自己想要的逻辑
目前Robust支持两种字节码插桩方案AspectJJavasist

腾讯 Tinker dexdiff / DexMerge方案

Tinker 是腾讯自研的重启生效的Java层的实现

Tinker基于一个基准dex,以及修复bug之后的dex,使用dexdiff算法,计算出差分包dex。然后把差分包推送给客户端,客户端收到之后,重启运行app,把差分包dex和原dex进行合并,形成新的dex。然后ClassLoader去创建类class对象的时候,就会创建修复bug之后的类class对象。从而达到修复bug的目的。

Dexdiff算法

了解增量更新的人应该知道bsdiffbsdiff是无视文件格式,生成两个二进制文件的差异文件。dexdiff是基于bsdiff,并且进行了针对dex文件格式的优化。 Tinker除了支持Dex修复之外,还支持so修复,只不过so的差分包,是直接使用的bsdiff算法生成的。

腾讯 QZone Muitidex方案

Multidex方案是基于ClassLoader纯java实现重启生效的热修复方案

原理:Apk打包的时候可能生成多个classes.dex文件。JVM中类加载器ClassLoader,在程序运行使用到某一个Class的时候,是按照顺序查找的方式进行的。一旦找到,就会缓存起来,下一次loadClass就不会去查找,而是直接使用缓存中的(所以Muitldex方案必须重启APP)。而当我们把补丁dex文件放到顺序查找的最前面,那么类加载器查找到它之后,就会直接使用。原来的同样的类便处于后方,不会再生效。由此,使用补丁Class完全替换了原Class

方案对比

Tinker QZone AndFix Robust
类替换 yes yes no no
so替换 yes no no no
资源替换 yes yes no no
全平台支持 yes yes yes yes
即时生效 no no yes yes
性能损耗 较小 较大 较小 较小
补丁包大小 较小 较大 一般 一般
开发透明 yes yes no no
复杂度 较低 较低 复杂 复杂
Gradle支持 yes no no no
Rom体积 较大 较小 较小 较小

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :