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

news/2025/2/28 17:59:07/

【原文地址 点击打开链接】

【各自热修复框架介绍 点击打开链接】

【AndFix使用说明 AndFix使用说明】

AndFix与Nuwa对比

Nuwa是另一个热补丁框架,原理是基于QQ空间团队提出的安卓App热补丁动态修复技术介绍。
与Nuwa相比,AndFix有一下优点:

  • 1、不需要重启APP即可应用补丁。
  • 2、安全性更好,Nuwa后面的版本应该也会加上安全方面的内容。

但是也有缺点

  • 1、无法添加类和字段
  • 2、Nuwa成功率高

线上程序出现bug,必须要有妙手回春的本事,想成为一名Andorid高工,这是必备技能。
Android 热补丁动态修复框架小结
Android热补丁动态修复技术系列 
安卓App热补丁动态修复技术介绍

现在市面上热修复框架很多,以下是常见的四种。

 TinkerQZoneAndFixDexposed
类替换yesyesnono
So替换yesnonono
资源替换yesyesnono
全平台支持yesyesyesno
即时生效nonoyesyes
性能损耗较小较大较小较小
补丁包大小较小较大一般一般
开发透明yesyesnono
复杂度较低较低复杂复杂
gradle支持yesnonono
接口文档丰富较少一般较少
Rom体积Dalvik较大较小较小较小
成功率较高最高一般一般

总的来说:

  1. Dexposed无法支持全平台,并不适合应用到商业产品中。
  2. AndFix作为native解决方案,首先面临的是稳定性与兼容性问题,更重要的是它无法实现类替换,它是需要大量额外的开发成本的。
  3. QZone方案主要问题是插桩带来Dalvik的性能问题,以及为了解决Art下内存地址问题而导致补丁包急速增大的。
    Tinker热补丁方案不仅支持类、So以及资源的替换,它还是2.X-7.X的全平台支持。它无需插桩,补丁大小也远远小于其他方案。Tinker已运行在微信的数亿Android设备上。

下面这个项目是一个基于热修复框架Tinker的Demo,并总结了一些常见的问题。


tinker.gif

项目地址https://github.com/xinghongfei/Hello-Tinker


1.背景

当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App、测试、向各个应用市场和渠道换包、提示用户升级、用户下载、覆盖安装。有时候仅仅是为了修改了一行代码,也要付出巨大的成本进行换包和重新发布。

这时候就提出一个问题:有没有办法以补丁的方式动态修复紧急Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装?

虽然Android系统并没有提供这个技术,但是很幸运的告诉大家,答案是:可以,我们QQ空间提出了热补丁动态修复技术来解决以上这些问题。


2.实际案例

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


3.解决方案

该方案基于的是android dex分包方案的,关于dex分包方案,网上有几篇解释了,所以这里就不再赘述,具体可以看这里https://m.oschina.net/blog/308583。

简单的概括一下,就是把多个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方案实现。然后在插入数组的时候,把补丁包插入到最前面去。

好,看似问题很简单,轻松的搞定了,让我们来试验一下,修改某个类,然后打包成dex,插入到classloader,当加载类的时候出现了(本例中是QzoneActivityManager要被替换):


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

从log的意思上来讲,ModuleManager引用了QzoneActivityManager,但是发现这这两个类所在的dex不在一起,其中:

1. ModuleManager在classes.dex中

2. QzoneActivityManager在patch.dex中

结果发生了错误。

这里有个问题,拆分dex的很多类都不是在同一个dex内的,怎么没有问题?

让我们搜索一下抛出错误的代码所在,嘿咻嘿咻,找到了一下代码:


从代码上来看,如果两个相关联的类在不同的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库来进行字节码插入的。

隐患:

虚拟机在安装期间为类打上CLASS_ISPREVERIFIED标志是为了提高性能的,我们强制防止类被打上标志是否会影响性能?这里我们会做一下更加详细的性能测试.但是在大项目中拆分dex的问题已经比较严重,很多类都没有被打上这个标志。

如何打包补丁包:

1. 空间在正式版本发布的时候,会生成一份缓存文件,里面记录了所有class文件的md5,还有一份mapping混淆文件。

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

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




http://www.ppmy.cn/news/395568.html

相关文章

电脑修复损坏文件--修复命令;系统盘磁盘空间不足

目录 sfc/scannow--修复损坏的系统文件命令解析command parse步骤 命令修复磁盘;磁盘空间的释放输入“sfc /purgecache”命令(不含双引号,下同)输入“cleanmgr”命令输入“CHKDSK D:/F”,修复D驱动器号 sfc/scannow–修…

基于QQ空间热修复原理实践

基于QQ空间热修复原理实践 关于热修复技术,去年真是火的一塌糊涂,俺们没有及时赶上,好在现在赶上也不算晚,好了废话不多说,直接进入正题。 原理: 简单阐述一下,具体的还是看原文吧。 说白了…

QQ空间无法修改“空间描述”修复方法

今天修改QQ空间描述,提示对话框“用户信息更新失败!”弄得我满处百度,几般尝试,可就是无法修改描述。结果,果断想起是不是没修改资料,跑去QQ个人中心官网,把星座那些刚才改生日自动填写的资料全…

QQ空间里面的照片变模糊了怎么办?时间越久越模糊怎么修复清晰?

很多人是不是都拿QQ空间当图片存档的地方,照片放进去你以为可以不管了,可以永久保存了!哪知道图片越放越模糊,甚至10年前的照片居然全成了马赛克,根本看不清楚了,哭都没地方去。 网上许多网友在看为什么存…

QQ空间热修复原理深入解析

一、背景 App的上线发布是我们程序猿开心的事情,证明着一段时间来成果的进步和展现。但是随着App的上线手机App市场,接下来的更新维护工作便成了”家常便饭“。尤其是在创业公司,随着业务等不稳定性因素,前期App的更新工作更为频繁,可能两天一小改,三天一大改的情况经常发…

Qzone 超级补丁热修复方案原理

介绍 Qzone 超级补丁技术基于dex分包方案,使用了多dex加载(multidex)的原理,大致的过程就是:把BUG方法修复以后,放到一个单独的dex文件,然后插入到dexElements数组的最前面,让虚拟机去加载修复完后的方法。…

QQ空间热修复思想学习总结

参考资料: 安卓App热补丁动态修复技术介绍 Android dex分包方案 一、什么是热修复 在没有热修复之前,我们发布一个app,用户安装到自己的手机上之后,突然发现一个小小的错误,比如一个显示错误,或者一个活动启动逻辑…

汉字转阿拉伯数字

汉字转阿拉伯数字 //数字HashMap<Character, Integer> numberMap new HashMap<Character, Integer>() {{put(一, 1);put(二, 2);put(三, 3);put(四, 4);put(五, 5);put(六, 6);put(七, 7);put(八, 8);put(九, 9);}};//单位HashMap<Character, Integer> uni…