首页 > qq购买平台 > 安卓App热补丁动态修复技术介绍
2022
07-30

安卓App热补丁动态修复技术介绍

作者:johnczchen

出品:QQ空间终端开发团队

原文发布于QQ空间终端开发团队之前,空间终端开发团队的官方账号都必须联系我。


当一个App发布后,突然发现一个严重的bug需要紧急修复。这时,公司各方都会忙得不可开交:重新打包App、对各应用市场和渠道进行测试、更换包装,提示用户升级、用户下载、覆盖安装。有时候只是为了修改一行代码,换包和重新发布要花很多钱。

提出一个问题:有没有办法通过补丁动态修复紧急情况?Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装?

虽然Android该系统没有提供该技术,但很幸运地告诉你,答案是:可以,我们QQ该空间提出了热补丁动态修复技术来解决题。

2.实际案例

空间Android独立版5.2发布后,收到用户反馈,组合版无法跳转到独立版的访客界面,每天反馈较大。以前只能紧急换包,重新发布。成本很高,也影响用户的口碑。最终决定采用热补丁动态修复技术,向用户下发Patch,用户无意识地修复外网问题,取得了很好的效果。

3.解决方案

该方案以此为基础android dex关于分包方案dex分包方案,网上有几个解释,这里就不赘述了。具体来说,你可以在这里看到Android dex分包方案。

简单总结一下,就是多个dex文件塞入到app的classloader之中,但是android dex如果拆包方案中的类别没有重复,如果classes.dex和classes1.dex有重复类,当使用这个重复类时,系统会选择加载哪个类?

让我们来看看类加载的代码:

一个ClassLoader可包含多个dex文件,每个dex文件是一个Element,多个dex将文件排列成有序的数组dexElements,找类的时候会按顺序遍历dex文件,然后从当前遍历dex在文件中找到类,如果找到类,则返回,如果找不到下一个dex继续搜索文件。

理论上,如果是不同的dex如果有相同的类,会优先考虑排在前面的。dex如下图所示:

在此基础上,我们构思了热补丁方案,将有问题的类别打包成一个dex(patch.dex)中去,然后把这个dex插入到Elements前面,如下图所示:

好的,该方案是基于第二次拆分dex如果解决方案,如果解决方案dex如果没有拆分的原则,每个人都应该很快实现这个计划。dex如果是项目,可以参考谷歌multidex方案实现。然后在插入数组时,将补丁包插入前面。

qq全新小号商城批发

好吧,看起来问题很简单,很容易解决。让我们试试,修改某一类,然后打包成dex,插入到classloader,当加载类出现时(本例是QzoneActivityManager更换):

为什么会出现以上问题呢?

从log从意义上说,ModuleManager引用了QzoneActivityManager,但是我发现了这两个类别的位置dex不在一起,其中:

1. ModuleManager在classes.dex中

2. QzoneActivityManager在patch.dex中

结果出了错qq全新小号商城批发误。

这里有一个问题,拆分dex许多类别不在同一个dex为什么内部没有问题?

让我们搜索抛出错误代码的地方,找到代码:

qq全新小号商城批发
从代码的角度来看,如果两个相关类别不同dex中间会报错,但拆分dex没有报错这是为什么,本次验证的前提是:

若引用者(即ModuleManager)这个类被打上了CLASS_ISPREVERIFIED标志,然后就会进行dex的校验。那这个标志是什么时候被打上的呢?

让我们继续搜索代码。~~,在DexPrepare.cpp找到代码:

这段代码是dex转化成odex(dexopt)我们知道代码中的一段是一个apk安装时,apk中的classes.dex会被虚拟机(dexopt)优化成odex然后拿去执行文件。

虚拟机启动时,会有很多启动参数,其中之一就是verify选项,当verify当选项打开时,上面doVerify变量为true,然后执行dvmVerifyClass如果dvmVerifyClass验证成功,所以这类会被打上CLASS_ISPREVERIFIED具体的验证过程是什么?

此代码在DexVerify.cpp中,如下:

1. 验证clazz->directMethods方法,directMethods包括以下方法:

1. static方法

2. private方法

3. 构造函数

2. clazz->virtualMethods

1. 虚函数=override方法?

概括一下就是如果以上方法中直接引用到的类(第一层级关系,不会进行递归搜索)和clazz都在同一个dex如果是中文,那么这一类就会被击中CLASS_ISPREVERIFIED

因此,为了实现补丁方案,必须从这些方法入手,防止类被击中CLASS_ISPREVERIFIED标志。

最终空间的方案是将代码插入到所有类别的构造函数中,代码如下:

if (ClassVerifier.PREVENT_VERIFY){

System.out.println(AntilazyLoad.class);

}

其中AntilazyLoad类会单独包装hack.dex,这样当安装apk的时候,classes.dex在不同的范畴中引用一个类dex中的AntilazyLoad类,防止类被击中CLASS_ISPREVERIFIED标志,只要没有标记的类别可以进行补丁操作。

然后在应用程序启动时加载.AntilazyLoad类所在的dex包必须先加载,否则,AntilazyLoad即使后续加载,类别也会被标记为不存在hack.dex包,那么他也不存在,这样屏幕就会出现很多类别AntilazyLoad找不到的log。

所以Application该代码不能插入作为应用程序的入口。(因为载入hack.dex的代码是在Application中onCreate如果在中执行Application这个代码插入到构造函数中,所以它是hack.dex加载前使用此类,一次找不到,会被永远打上找不到的标志)

其中:

之所以选择构造函数,是因为它不增加方法数,即使一个类没有显式构造函数,也会有隐式的默认构造函数。

空间使用代码插入字节码,而不是源代码,使用javaassist插入字节码的库。

空间使用代码插入字节码,而不是源代码,使用javaassist插入字节码的库。虚拟机在安装过程中类打

CLASS_ISPREVERIFIED

标志是为了提高性能,我们强制防止标志会影响性能吗?在这里,我们将进行更详细的性能测试。但在大型项目中拆分dex问题已经比较严重了,很多类别都没有被贴上这个标志。

如何打包补丁包:

1. 当正式版本发布时,空间将生成一个记录所有的缓存文件class文件的md5,还有一份mapping混淆文件。

2. 使用后续版本-applymapping选项,应用正式版本mapping文件,然后计算编译完成后的文件class文件的md5.比较正式版本,比较不同的版本class文件包装成补丁包。

注:该方案现在也应用于我们的编译过程中,编译不需要重新包装dex,只需要把修改过的类的class文件打包成patch dex,然后放到sdcard然后,变更代码将生效。

关于Qzone :

Qzone 腾讯集团是中国最大的社交网络和核心平台之一Qzone月活跃账户数达到6.68亿,Qzone智能终端月活跃账户5个.68亿。从2005~2015,Qzone见证了国内互联网蓬勃发展的十年,但我们的业务不断发展。我们也希望更多的朋友能加入我们,共同欢迎互联网Qzone接下来的十年。

欢迎关注我们Qzone微信官方账号终端开发团队:

本文》有 0 条评论

留下一个回复