文章目录
一、 PathClassLoader 加载 Dex 机制
二、 PathDexList 查找 Class 机制
三、 类查找的顺序机制
一、 PathClassLoader 加载 Dex 机制
Android 中 Class 加载机制 :
Java 代码运行时 , 使用 ClassLoader 加载 Class 字节码文件 , Class 字节码文件 , Jar 文件 , Dex 文件 , 都必须加载到内存中 ;
在 Android 平台中 , Android 应用运行时 , 使用 PathClassLoader 加载 Dex 文件 , 在应用启动时 , 首先将若干 Dex 文件加载到内存中 ;
一个 Android 应用中可以存在多个 Dex 文件 , 参考 【Android 安全】DEX 加密 ( 多 DEX 加载 | 65535 方法数限制和 MultiDex 配置 | PathClassLoader 类加载源码分析 | DexPathList ) 博客 ,
二、 PathDexList 查找 Class 机制
当应用运行时调用到某类 A.class 时 , 会通过 PathClassLoader 加载该类 , PathClassLoader 是一个包装类 , 其中封装了 PathDexList 类 , 该类中的 Element[] dexElements 成员存放着多个 Dex 文件 ;
每个 Dex 文件中封装了多个 Class 字节码文件 ; 查找某个具体的 A.class 时 , 主要是通过 DexPathList 的 findClass 方法 , 遍历 Element[] dexElements 成员 ,
Element[] dexElements 数组中保存的就是内存中的 DEX 文件 , 如果 APP 中有 3 33 个 DEX 文件 , 那么该数组就有 3 33 个元素 ;
然后逐个遍历 获取该 element 中的 dexFile , 这是 DexFile 类型文件 ,
调用 DexFile 的 loadClassBinaryName 加载对应的 A.class 类 , 如果找到了 A.class 类 , 直接返回 ; 如果没有找到 , 则继续遍历下一个 Element[] dexElements 元素 ;
/*package*/ final class DexPathList { /** * dex/resource (class path) 元素集合. * 应该调用 pathElements , 但是 Facebook 应用通过反射修改 dexElements . */ private final Element[] dexElements; public Class findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); // 注意 : 这里如果查找到想要的类 , 直接返回 , 不会去向后遍历 if (clazz != null) { return clazz; } } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; } }
参考源码地址 : libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
三、 类查找的顺序机制
在 PathDexList 查找 Class 中 , DexPathList 的 findClass 方法遍历 Element[] dexElements 数组永远都是从 0 00 索引开始 , 向后遍历 , 其中就隐含着 Dex 文件搜索的顺序机制 ;
顺序机制 : 如果在第 2 22 个 Dex 文件 和 第 3 33 个 Dex 文件中都包含该 A.class 字节码文件 , 则只会返回第 2 22 个 Dex 文件中的类 , 遍历到此处就返回了 , 不会去遍历到低 3 33 个 Dex 文件 ;
利用上述顺序加载的机制 , 将修复包的 dex 文件放在前面 , 就可以屏蔽后面出现问题的类 ;
示例 :
在已经发布的应用中 , 有 3 33 个 Dex 文件 , 其中第 2 22 个 Dex 文件中的 A.class 字节码类中出现了崩溃问题 , 这里我们将修复好的 A.class 文件打包成 Dex 文件 , 将其插入到原来第 1 11 个和第 2 22 个 Dex 文件之间 , 这样在类加载器加载 A.class 类时 , 按照顺序先把修复的 Dex 文件加载到内存中 , 不再向后查找第 3 33 个 Dex 文件中出现崩溃的 A.class 类了 ;
热修复只是在前面插入一个修复好的 Dex 文件 , 不会删除出现问题的 Dex 文件 ;
第 1 11 个 Dex 文件一定不能出现问题 , 如果出现问题 , 就无法在该 Dex 文件之气前插入 Dex 文件 , 这个主 Dex 文件中有 Application , MainActivity 等相关重要组件 , 无法进行替换 ;
如果第 1 11 个 Dex 文件出现问题 , 只能发布新版本 ;
实际上热修复机制属于一种利用漏洞的机制 , Google 没有正面支持该功能 , iOS 中就关闭了热修复功能 ;