当前市面的热补丁方案有很多,其中比较出名的有阿里的AndFix、美团的Robust以及QZone的超级补丁方案。
但它们都存在无法解决的问题,这也是正是推荐Tinker的原因。
Tinker | QZone | AndFix | Robust | |
---|---|---|---|---|
类替换 | yes | yes | no | no |
So替换 | yes | no | no | no |
资源替换 | yes | yes | no | no |
全平台支持 | yes | yes | yes | yes |
即时生效 | no | no | yes | yes |
性能损耗 | 较小 | 较大 | 较小 | 较小 |
补丁包大小 | 较小 | 较大 | 一般 | 一般 |
开发透明 | yes | yes | no | no |
复杂度 | 较低 | 较低 | 复杂 | 复杂 |
gradle支持 | yes | no | no | no |
Rom体积 | 较大 | 较小 | 较小 | 较小 |
成功率 |
图解修复过程
image.png原理:
当我们在java代码当中new 一个对象的时候,会通过PathClassLoader去执行类加载工作,但是通过阅读源码发现,PathClassLoader其实只是一个包装类,是不干实事的,最终是通过调用
BaseDexClassLoader中的PathDexList对象的findClass方法进行加载,而PathDexList中的Elements[]存储的则是我们的dex文件(classes.dex,classes2.dex...)
public class BaseDexClassLoader extends ClassLoader {
// 需要加载的dex列表
private final DexPathList pathList;
// dexPath要加载的dex文件所在的路径,optimizedDirectory是odex将dexPath
// 处dex优化后输出到的路径,这个路径必须是手机内部路劲,libraryPath是需要
// 加载的C/C++库路径,parent是父类加载器对象
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
// 使用pathList对象查找name类
Class c = pathList.findClass(name, suppressedExceptions);
return c;
}
}
final class DexPathList {
//在这个地方会遍历我们的Element数组,直到找到我们需要Class
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
}
通过DexPathList 的findclass方法,会遍历Elements数组,直到找到我们指定的class。
热修复便是基于该顺序遍历的方式,通过在Elements的头部插入我们修复好的dex,当我们查找class的时候,由于在Elements头部找到了我们所需的class,后面有问题的便不会进行加载。
步骤:
- 将修改好的java文件转为dex包,上传到服务端;
- app下载好服务端上传的dex包到sd卡目录;
- 将下载好的dex包转移到odex目录下,同时将其存储到BaseDexClassLoader中的PathDexList对象当中的dexElements数组当中。
- 结束
网友评论