【新技能get】让App像Web一样发布新版本
2015.10.22 腾讯Bugly [微信分享](http://bugly.qq.com/blog/?p=781#) 背景 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App、测试、向各个应用市场和渠道换包、提示用户升级、用户下载、覆盖安装。有时候仅仅是为了修改了一行代码,也要付出巨大的成本进行换包和重新发布。 这时候就提出一个问题:**有没有办法以补丁的方式动态修复紧急Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装?** 虽然Android系统并没有提供这个技术,但是很幸运的告诉大家,答案是:可以。 解决方案 该方案基于的是android dex分包方案的,关于dex分包方案,网上有几篇解释了,所以这里就不再赘述,具体可以看这里:https://m.oschina.net/blog/308583(请复制链接到浏览器打开)。 简单的概括一下,就是把多个dex文件塞入到app的classloader之中,但是android dex拆包方案中的类是没有重复的,如果classes.dex和classes1.dex中有重复的类,当用到这个重复的类的时候,系统会选择哪个类进行加载呢? 让我们来看看类加载的代码: `<strong>public</strong> Class findClass(String name, List<Throwable> suppressed) { <strong>for</strong> (Element element : dexElements) { //每个Element就是一个dex文件 DexFile dex = element.dexFile; <strong>if</strong> (dex != <strong>null</strong>) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); <strong>if </strong>(clazz != <strong>null</strong>) { <strong> return </strong>clazz; } } } <strong> if </strong>(dexElementsSuppressedExceptions != <strong>null</strong>) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } <strong> return null</strong>; } ` 一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element,多个dex文件排列成一个有序的数组dexElements,当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找类则返回,如果找不到从下一个dex文件继续查找。 理论上,如果在不同的dex中有相同的类存在,那么会优先选择排在前面的dex文件的类,如下图:  在此基础上,我们构想了热补丁的方案,把有问题的类打包到一个dex(patch.dex)中去,然后把这个dex插入到Elements的最前面,如下图  好,该方案基于第二个拆分dex的方案,方案实现如果懂拆分dex的原理的话,大家应该很快就会实现该方案,如果没有拆分dex的项目的话,可以参考一下谷歌的multidex方案实现。然后在插入数组的时候,把补丁包插入到最前面去。 好,看似问题很简单,轻松的搞定了,让我们来试验一下,修改某个类,然后打包成dex,插入到classloader,当加载类的时候出现了(本例中是ActivityManager要被替换):  **为什么会出现以上问题呢?** **从log的意思上来讲,ModuleManager引用了ActivityManager,但是发现这这两个类所在的dex不在一起,其中:** **1. ModuleManager在classes.dex中** **2. ActivityManager在patch.dex中** **结果发生了错误。** **这里有个问题,拆分dex的很多类都不是在同一个dex内的,怎么没有问题?** **让我们搜索一下抛出错误的代码所在,嘿咻嘿咻,找到了一下代码:**  从代码上来看,如果两个相关联的类在不同的dex中就会报错,但是拆分dex没有报错这是为什么,原来这个校验的前提是:  如果引用者(也就是ModuleManager)这个类被打上了**CLASS_ISPREVERIFIED标志**,**那么就会进行dex的校验。那么这个标志是什么时候被打上去的?** **让我们在继续搜索一下代码,嘿咻嘿咻~~,在DexPrepare.cpp找到了一下代码:** ** ...