Android HotFix动态加载框架介绍
HotFix(Deprecated)
https://github.com/dodola/HotFix
请关注 RocooFix
我重新写了一个RocooFix框架,解决了Nuwa因为Gradle1.40 里Transform API无法打包的情况,现在兼容Gradle1.3-Gradle2.1.0版本
安卓App热补丁动态修复框架
介绍
该项目是基于QQ空间终端开发团队的技术文章实现的,完成了文章中提到的基本功能。
文章地址:安卓App热补丁动态修复技术介绍
项目部分代码从 dalvik_patch 项目中修改而来,这个项目本来是用来实现multidex的,发现可以用来实现方法替换的效果。
项目包括核心类库,补丁制作库,例子。可以直接运行代码看效果。
文章作者Github: jiqimaogou
类似项目: Nuwa 这个项目补丁自动化那块做的很完整,感兴趣的可以去看
详细说明
补丁制作
该技术的原理很简单,其实就是用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 这个类

补丁加载过程分析
关于混淆
文档里已经给出了解决方案。
- 在正式版本发布的时候,会生成一份缓存文件,里面记录了所有class文件的md5,还有一份mapping混淆文件。
- 在后续的版本中使用-applymapping选项,应用正式版本的mapping文件,然后计算编译完成后的class文件的md5和正式版本进行比较,把不相同的class文件打包成补丁包。
总的来说就是从有补丁功能的版本开始,保存一份mapping混淆文件,后续编译用同一个混淆mapping文件,这样就能保证混淆过后的类名始终是一致的。
这样打补丁混淆就能完全的自动化了。
ISSUE
- 开发测试过程中遇到一些问题,这种方法无法在已经加载好的类中实现动态替换,只能在类加载之前替换掉。就是说,补丁下载下来后,只能等待用户重启应用才能完成补丁效果。
- 有同学反馈在一加手机上会出现
Class ref in pre-verified class resolved to unexpected的错误,待找到手机后修复。。
Android HotFix动态加载框架介绍的更多相关文章
- Android RocooFix热修复动态加载框架介绍
RocooFix Another hotfix framework 之前的HotFix项目太过简单,也有很多同学用Nuwa遇到很多问题,作者也不再修复,所以重新构建了一套工具. Bugfix 2016 ...
- Android 使用动态加载框架DL进行插件化开发
http://blog.csdn.net/t12x3456/article/details/39958755/ 转载自: 时之沙: http://blog.csdn.net/t12x3456
- Android动态加载框架汇总
几种动态加载的比较 1.Tinker 用途:热修复 GitHub地址:https://github.com/Tencent/tinker/ 使用:http://www.jianshu.com/p/f6 ...
- 携程Android App的插件化和动态加载框架
携程Android App的插件化和动态加载框架已上线半年,经历了初期的探索和持续的打磨优化,新框架和工程配置经受住了生产实践的考验.本文将详细介绍Android平台插件式开发和动态加载技术的原理和实 ...
- APK动态加载框架(DL)解析
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/39937639 (来自singwhatiwanna的csdn博客) 前言 好久 ...
- 动态加载框架DL分析
动态加载框架DL分析 插件化开发,主要解决三个问题1.动态加载未安装的apk,dex,jar等文件2.activity生命周期的问题,还有service3.Android的资源调用的问题 简单说一下怎 ...
- Android之Android apk动态加载机制的研究(二):资源加载和activity生命周期管理
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/23387079 (来自singwhatiwanna的csdn博客) 前言 为了 ...
- Android之图片加载框架Fresco基本使用(一)
PS:Fresco这个框架出的有一阵子了,也是现在非常火的一款图片加载框架.听说内部实现的挺牛逼的,虽然自己还没研究原理.不过先学了一下基本的功能,感受了一下这个框架的强大之处.本篇只说一下在xml中 ...
- Android之Android apk动态加载机制的研究
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/22597587 (来自singwhatiwanna的csdn博客) 背景 问题 ...
随机推荐
- Tomcat(五):nginx/httpd + tomcat及负载均衡tomcat
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- redis通过pipeline提升吞吐量
案例目标 简单介绍 redis pipeline 的机制,结合一段实例说明pipeline 在提升吞吐量方面发生的效用. 案例背景 应用系统在数据推送或事件处理过程中,往往出现数据流经过多个网元: 然 ...
- 什么是<!DOCTYPE html>
什么是<!DOCTYPE html>? 在HTML文档初,往往会有这么一句话<!DOCTYPE html>,那么它的意义是什么呢?它是html5标准网页声明,全称为Docume ...
- 基于 HTML5 Canvas 绘制的电信网络拓扑图
电信网结构(telecommunication network structure)是指电信网各种网路单元按技术要求和经济原则进行组合配置的组合逻辑和配置形式.组合逻辑描述网路功能的体系结构,配置形式 ...
- [DeeplearningAI笔记]改善深层神经网络_深度学习的实用层面1.9_归一化normalization
觉得有用的话,欢迎一起讨论相互学习~Follow Me 1.9 归一化Normaliation 训练神经网络,其中一个加速训练的方法就是归一化输入(normalize inputs). 假设我们有一个 ...
- C#之DataTable转List与List转Datatable
闲来无事,只有写代码啦,以下为DataTable转List与List转DataTable的两个方法,主要技术点用到了反射原理: /// <summary> /// 模型转换类 /// &l ...
- SAP字符串处理
拼接字符串 CONCATENATE t1 t2 INTO result. "直接拼接 CONCATENATE t1 t2 INTO result SEPARATED BY space. &q ...
- bzoj 4815: [Cqoi2017]小Q的表格 [数论]
4815: [Cqoi2017]小Q的表格 题意: 单点修改,查询前缀正方形和.修改后要求满足条件f(a,b)=f(b,a), b×f(a,a+b)=(a+b)*f(a,b) 一开始sb了认为一次只会 ...
- BZOJ 4176: Lucas的数论 [杜教筛]
4176: Lucas的数论 题意:求\(\sum_{i=1}^n \sum_{j=1}^n \sigma_0(ij)\) \(n \le 10^9\) 代入\(\sigma_0(nm)=\sum_{ ...
- BZOJ 3550: [ONTAK2010]Vacation [单纯形法]
有3N个数,你需要选出一些数,首先保证任意长度为N的区间中选出的数的个数<=K个,其次要保证选出的数的个数最大. 好像都是费用流... 单纯性裸题呀... 注意每个数最多选1次 #include ...