Android热修复主流方案
热修复方案按照是否必须重启分为两类:重启生效 / 即时生效。按照实现方式可以分为3类:Java层的实现 / Native层的实现 / Java Native混合实现
阿里AndFix 方案(已弃用)
AndFix 是 无需重启 的 Native层 的实现。但是,AndFix目前已经3年多没维护更新。
因为阿里已经有了新的替代方案,不再需要维护。另外,这种纯Native的实现方式,兼容性十分堪忧。
既然已经被弃用,但是稍微了解一下实现方案也是没问题的。
假设有一段程序按照A=>B=>C
的顺序去执行,然而发现执行到B的时候,存在一个bug,AndFix的修复方式为:在Native层执行到B方法的时候,把方法的指针指向了补丁包里的B方法,绕过了原来的B方法。从而达到了修复bug的目的。
//注解表示补丁中此方法会替换apk中哪个方法
@MethodReplace(clazz="cn.appblog.utils", method="test")
public void test() {
...
}
在需要修复的方法上面,加上@MethodReplace
注解,给定参数class
和method
,表示该方法替换为哪个类的哪个方法。
缺陷:修复粒度为:method
。AndFix只能以方法为粒度去修复bug。作用有限,针对大范围的类替换和资源替换,那就无能为力。
美团 Robust方案
Robust 是即时生效的Java层实现的热修复实现方案.
美团的Robust方案,是参考了谷歌的InstantRun方案(之前在Android Studio里面有这个选项,可以选择打开Instant Run运行app)的思路而设计出来的。
其主要设计思想就是一句话:编译打包时,在程序的某些方法里面,都插入一段代码(全自动操作):
if (changeQuickRedirect != null) {
//
return 修复的实现
}
当changeQuickRedirect
不为空的时候,该方法就会命中if (changeQuickRedirect != null)
,从而执行修复的实现代码。当为空的时候,则正常执行原逻辑。
而平时我们自己编码,只需要加上一个@Modify
注解,来标记这个方法需要打补丁包,也就是需要插入上面这个if (changeQuickRedirect != null)
代码段。
//编写的代码
@Modify //改动代码后手动添加注解用于补丁包生成
public long getIndex() {
return 100;
}
(1)为什么Robust可以即时生效?
上图解析:利用ClassLoader
,当客户端手机收到补丁包 patch.dex
的时候,执行补丁包,把指定方法的changeQuickRedirect
用反射的手段赋值,让它变成非空。从而让下一次程序逻辑走到这里的时候,走修复之后的逻辑。
(2)Robust是如何将这么多if (changeQuickRedirect != null)
代码段插入到代码逻辑中的?
上图中的if (changeQuickRedirect != null)
代码段,并非我们手动编写,而是由Robust框架自动插入的
这个技术叫做字节码插桩,意为:对class进行操作,按照class文件的格式,插入自己想要的逻辑
目前Robust支持两种字节码插桩方案AspectJ
和Javasist
腾讯 Tinker dexdiff / DexMerge方案
Tinker 是腾讯自研的重启生效的Java层的实现
Tinker基于一个基准dex,以及修复bug之后的dex,使用dexdiff
算法,计算出差分包dex。然后把差分包推送给客户端,客户端收到之后,重启运行app,把差分包dex和原dex进行合并,形成新的dex。然后ClassLoader
去创建类class对象的时候,就会创建修复bug之后的类class对象。从而达到修复bug的目的。
Dexdiff算法
了解增量更新的人应该知道bsdiff
。bsdiff
是无视文件格式,生成两个二进制文件的差异文件。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体积 | 较大 | 较小 | 较小 | 较小 |
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/29/android-hot-fix-mainstream-solution/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论