开源框架地址
https://github.com/dodola/HotFix
实现原理来自:安卓App热补丁动态修复技术介绍,在这就不解释了。
一 配置项目
首先将开源项目中的buildSrc、hackdex、hotfixlib这三个Import Module到自己的项目中。
第二步将开源项目中assets下的hackdex_dex.jar复制到自己项目assets下。
第三步在app下的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所在目录
}
applicationVariants.all { variant ->
variant.dex.dependsOn << processWithJavassist //在执行dx命令之前将代码打入到class中
}
注意项目编译class所在的目录如果是正式打包的版本是在这个文件夹下
task('processWithJavassist') << {
String classPath = file('build/intermediates/classes/release')//项目编译class所在目录
dodola.patch.Patchclass.process(classPath, project(':hackdex').buildDir
.absolutePath + '/intermediates/classes/release')//第二个参数是hackdex的class所在目录
}
有的会突然报这个错误
解决方法也很简单,把项目目录下的gradle改成1.3.0就可以了,版本太高task运行好像不行。
第四步在自己项目Application的onCreate中添加上一段代码
// 把assets目录下的hackdex_dex.jar插入到dexElements
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "hackdex_dex.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "hackdex_dex.jar");
HotFix.patch(this, dexPath.getAbsolutePath(), "dodola.hackdex.AntilazyLoad");
try {
this.getClassLoader().loadClass("dodola.hackdex.AntilazyLoad");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
// 如果有补丁包就加载补丁包 在APP接到打补丁通知后 退出前下载到自定义目录下
// 在这里是根目录下的Download目录下 补丁包起名叫做eclair_dex.jar
File newDexPath = new File(Environment.getExternalStorageDirectory() + "/Download/eclair_dex.jar");
if (newDexPath.exists()) {
Utils.prepareDex(this.getApplicationContext(), newDexPath, "eclair_dex.jar");
HotFix.patch(this, newDexPath.getAbsolutePath(), "");
}
} catch (Exception e) {
}
最后修改依赖的buildSrc中的PatchClass
List<String> list = new ArrayList<>();
list.add("com.eclair.activities.Card_Act");
list.add("com.eclair.activities.DigitalDetailsWeb_Act");
list.add("com.eclair.activities.MainActivity");
list.add("com.eclair.activities.Map_Act");
...
println(lib)
ClassPool classes = ClassPool.getDefault()
classes.appendClassPath(buildDir)
classes.appendClassPath(lib)
for (int i = 0; i < list.size(); i++) {
//下面的操作比较容易理解,在将需要关联的类的构造方法中插入引用代码
CtClass c = classes.getCtClass(list.get(i))
if (c.isFrozen()) {
c.defrost()
}
println("====添加构造方法====" + i)
def constructor = c.getConstructors()[0];
constructor.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);")
c.writeFile(buildDir)
}
这个文件的作用就是在编译的时候把.class文件的构造方法中加入System.out.println(dodola.hackdex.Antilazyload.class);
目的是为了防止被打上CLASS_ISPREVER IF IED标志。
因为不知道可能出现哪些BUG,所以我干脆把所有的类全都打上了 。。。哈哈哈
把类名存到list中循环插入
然后点击运行我们的项目,有时会报这种错误
然后根据输出 i 的位置找到出问题的类 发现
这个类的构造方法中传入了Context,既然不能这样插入,那我们就直接在这个类的构造方法中加入
然后就不需要在PatchClass文件中插入了
好啦,项目的配置完事了,我们来做个补丁测试一下吧~
二 制作补丁文件
首先,我们修改一下代码,在这里我将Music_Fragment中的
修改为
好啦然后我们从新运行/打包一下我们的App 然后在项目文件下的
app\build\intermediates\classes\release寻找我们刚才修改类的.class文件
如果是直接运行的,那么应该在
app\build\intermediates\classes\debug下
注意不能直接从Studio中复制!!
全部复制出来,然后,新建一个文件夹目录是这个类的包名例如 我这个类的所在包名是com.eclair.fragments
那么就新建一个com文件夹,里面再新建eclair然后再新建fragments,最后把复制的class文件放到fragments文件夹下
也可以直接将com目录复制出来,打成jar包的时候选择哪些.class文件
接下来在运行cmd :jar cvf 起的名字.jar com目录下所有文件
在这里我把com文件夹放在了D盘下的thishotfix文件夹中,cmd运行的时候也在D:thishotfix中
如果是全部复制出来的可以一个文件一个文件的选择 但是要一个不能落下 一个类可能会有很多.calss
我在这演示一下所以只添加了两个
每个文件之间用空格分开
jar cvf xxx.jar 文件 文件 文件
输入完成后回车,如果成功应该会是这样的
这时运行目录下应该多了一个 xxx.jar
现在,这个jar文件还不能直接当作补丁,需要dx工具转化一下,这个工具在你的Android SDK目录下的build-tools中,任意点开一个版本就可以看到dx.bat和lib文件夹下的dx.jar,如果没有,可以去万能的百度寻找,我们直接切换cmd目录到这里。
然后运行 dx –dex –output 你程序中约定好的补丁名字.jar 刚才生成的.jar
运行完成后会在当前目录找到这个jar,在这里我是eclair_dex.jar,这个就是做好的补丁文件。
然后运行我们之前的App,完全关闭之后再把补丁文件放到事先约定好的文件目录下,从新打开App你就会发现打上补丁了~
在这里是模拟下载完成从新开启软件打补丁