Android 热修复思路整理
问题提出
- 什么是热修复
- 热修复的优点
- 流行的热修复方式
- 本文采用的热修复
- 方式
- 相关知识点
- 思路
解决方案
-
热修复:
- Android端修复bug,通常方案是,修改bug所在的类->打包整个代码apk->重新上线->用户下载apk重新安装使用;
- 热修复的方式,修改bug所在的类->用户在已有app的情况下下载修复好的fixbug.class。
-
热修复的优点:很明显开发人员不需要重新打包整个apk重新上传apk,用户不需要重新下载整个apk,在用户使用已有app的时候,偷偷的将bug就修复了,用户甚至不知道发生过什么,提高用户体验。
-
流行的热修复方式(按技术特点划分)
- 通过底层二进制(C/C++)方式,阿里:DeXposed。andfix。
- 通过JAVA层加载机制, 腾讯:tinker
-
本文采用的热修复
- 比较容易理解的Java类加载机制实现的。
- 主要用到的技术:JAVA反射,热修复的热字就体现在这里,运行时fix bug...
- 思路
- 以前搞过反编译的小伙伴已经反编译过classes.dex文件,classes文件是如何生成的呢?这就好办了找到生成dex文件的地方就可以了。。。
- 其实JAVA类的加载机制是从:dalvik.system.BaseDexClassLoader开始的,查看源码我们发现里面使用了DexPathList这个类,这个类里面的是通过dexElements数组,来处理我们反编译出来的classes.dex, classes2.dex文件
- 如果在细看代码你会发现,已经加载过的.class文件就不会再被加载了,也就是说同一时刻ClassLoder只会加载一次同一个.class文件一次,那我们是不是可以在有问题的.class文件加在之前,先加载我们的修复好的.class文件是不是就可以了。
- 那剩下的问题就剩下如何获取到dexElements数组,将修复好的dex合并到dexElements数组里面,重新让系统使用即可。
- 那我们先来确认dex文件位于app那个位置呢,答案就是在当前app的getDir()目录下(data\data...),我们只要将修改好的java文件生成.class文件在编译成classes.dex文件再将该文件存入到该目录下,通过反射获取我们的classes.dex进行数组合并工作,就可以达到目的,下面着重说一下反射思路。(生成classes.dex文件可用:你的SDK目录\build-tools\24.0.3\dx.bat工具)
- 反射思路:
//加载应用程序的ClassLoder
ClassLoader classLoader = context.getClassLoader();
for (File file : loadedDex) {
//因为系统通过DexClassLoder来加载dex,所以需要将新加入的修复dex伪装成系统能够识别的ClassLoder
DexClassLoader dexClassLoder = new DexClassLoader(
file.getAbsolutePath(),// String dexPath,
optFile.getAbsolutePath(),// String optimizedDirectory,
null,//String libraryPath,
classLoader// ClassLoader parent
);
//分别通过系统和Fix Dex反射获取DexClassLoader对象的DexPathList对象
// 进一步获取DexPathList对象的dexElements数组对象
//将两个数组利用已经loaderd的类不会呗再次加载的特点,将fix数组放在dexElements数组下标0处,合并两个数组
//通过放射将修改后的dexElements数组重新写入到app加载类的目录下,达到修复的目的,此时错误的class文件还是存在与数组内部的,但是已经不会再被加载了
-
可能遇到的坑
- 需要将AS的Instant Run功能暂时关闭,不然修复的内容有可能无法写入到classes.dex看不到效果
- 需要配置APP的gradle里的mutidex