Android热补丁技术方案整理
2016-06-29 12:06
459 查看
概述
项目快速迭代过程中,不可避免的出现BUG,Android线上出现问题,通常需要发版解决。紧急发版,用户不一定升级,强制升级又不友好,有什么更好的解决方案呢?这就用到了热修复技术。QQ团队的hotfix
hotfix,后来发展成为RocooFix,GitHub地址: https://github.com/dodola/HotFix
原理详细介绍官方文章:安卓App热补丁动态修复技术介绍
HotFix存在的问题:这种方法无法在已经加载好的类中实现动态替换,只能在类加载之前替换掉。就是说,补丁下载下来后,只能等待用户重启应用才能完成补丁效果。
RocooFix支持两种模式:
静态修复某种情况下需要重启应用。
动态修复,无需重启应用即可生效。
补丁制作
该技术的原理很简单,其实就是用ClassLoader加载机制,覆盖掉有问题的方法。所以我们的补丁其实就是有问题的类打成的一个包。例子中的出现问题的类是
dodola.hotfix.BugClass原始代码如下:
public class BugClass { public String bug() { return "bug class"; } }
我们假设
BugClass类里的
bug()方法出现错误,需要修复,修复代码如下:
public class BugClass { public String bug() { return "fixed class"; } }
那么我们只需要将修复过的类编译后打包成dex即可
步骤如下:
将补丁类提取出来到一个文件夹里
将class文件打入一个jar包中
jar cvf path.jar *
将jar包转换成dex的jar包
dx --dex --output=path_dex.jar path.jar
这样就生成了补丁包
path_dex.jar
实现javassist动态代码注入
实现这一部分功能的原因主要是因为出现如下异常java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
问题原因在文档中已经描述的比较清楚。
就是如果以上方法中直接引用到的类(第一层级关系,不会进行递归搜索)和clazz都在同一个dex中的话,那么这个类就会被打上CLASS_ISPREVERIFIED
很明显,解决的方法就是在类中引用一个其他dex中的类,但是源码方式的引用会将引用的类打入同一个dex中,所以我们需要找到一种既能编译通过并且将两个互相引用的类分离到不同的dex中,于是就有了这个动态的代码植入方式。
首先我们需要制作引用类的dex包,代码在
hackdex中,我直接使用了文档中的类名
AntilazyLoad这样可以和文章中对应起来,方便一些。
我们将这个库打包成dex的jar包,方法跟制作补丁一样。
下面是重点,我们要用
javassist将这个类在编译打包的过程中插入到目标类中。
为了方便,我将这个过程做成了一个Gradle的Task,代码在
buildSrc中。
这个项目是使用Groovy开发的,需要配置Groovy SDK才可以编译成功。
核心代码如下:
/** * 植入代码 * @param buildDir 是项目的build class目录,就是我们需要注入的class所在地 * @param lib 这个是hackdex的目录,就是AntilazyLoad类的class文件所在地 */ public static void process(String buildDir, String lib) { println(lib) ClassPool classes = ClassPool.getDefault() classes.appendClassPath(buildDir) classes.appendClassPath(lib) //下面的操作比较容易理解,在将需要关联的类的构造方法中插入引用代码 CtClass c = classes.getCtClass("dodola.hotfix.BugClass") println("====添加构造方法====") def constructor = c.getConstructors()[0]; constructor.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);") c.writeFile(buildDir) CtClass c1 = classes.getCtClass("dodola.hotfix.LoadBugClass") println("====添加构造方法====") def constructor1 = c1.getConstructors()[0]; constructor1.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);") c1.writeFile(buildDir) growl("ClassDumper", "${c.frozen}") }
下面在代码编译完成,打包之前,执行植入代码的task就可以了。
在 app 项目的 build.gradle 中插入如下代码
task('processWithJavassist') << { String classPath = file('build/intermediates/classes/debug')//项目编译class所在目录 dodola.patch.PatchClass.process(classPath, project(':hackdex').buildDir .absolutePath + '/intermediates/classes/debug')//第二个参数是hackdex的class所在目录 } android{ ....... applicationVariants.all { variant -> variant.dex.dependsOn << processWithJavassist //在执行dx命令之前将代码打入到class中 } }
反编译编译后的apk可以发现,代码已经植入进去,而且包里并不存在
dodola.hackdex.AntilazyLoad这个类
阿里巴巴的AndFix
GitHub地址: https://github.com/alibaba/AndFix使用步骤
初始化patchManager = new PatchManager(context);
patchManager.init(appversion);//current version加载patch
patchManager.loadPatch();添加patch文件
patchManager.addPatch(path);支持热更新,不需要重新启动
阿里巴巴的dexposed
GitHub地址: https://github.com/alibaba/dexposed大众点评的Nuwa
Nuwa的具体实现也是根据QQ空间的热修复方案来实现的GitHub地址: https://github.com/jasonross/Nuwa
DroidFix
GitHub地址: https://github.com/bunnyblue/DroidFix官方介绍: http://bunnyblue.github.io/DroidFix/
DroidFix的实现原理跟QQ空间的热补丁方案类似。
携程的DynamicAPK
GitHub地址: https://github.com/CtripMobile/DynamicAPK主流热补丁开源方案基本上就是以上这些。
相关文章推荐
- andfix 热补丁修复
- iOS 通过 JSPatch 实时修复线上 bug!
- 各大热补丁方案分析和比较
- andfix 增量升级更新 热补丁修复
- Andfix热修复框架原理及源码解析-下篇
- 【转】Android 热补丁动态修复框架小结
- Linux热补丁的实现
- Hook原理分析
- iOS中动态更新补丁策略JSPatch运用基础一
- android 热补丁功能实现初探<一>
- Android 热修复框架 Tinker ( 二 )
- 一、将Tinke集成到自己的项目
- AndHotFix热修补使用总结
- 安卓App热补丁动态修复技术介绍
- TPatch动态补丁系统(iOS)
- Ubuntu14.04下kpatch源码安装使用
- 安卓热修复 Andfix&Tinker
- 微信热补丁Tinker -- 补丁流程
- 微信热补丁Tinker -- 项目集成
- Android 九年,我们需要学什么?