【Android 热修复】热修复原理 ( 类加载机制 | PathClassLoader 加载 Dex 机制 | PathDexList 查找 Class 机制 | 类查找的顺序机制 )

2023-03-29 09:39:56 浏览数 (1)

文章目录

  • 一、 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

个 DEX 文件 , 那么该数组就有

3

个元素 ;

然后逐个遍历 获取该 element 中的 dexFile , 这是 DexFile 类型文件 ,

调用 DexFile 的 loadClassBinaryName 加载对应的 A.class 类 , 如果找到了 A.class 类 , 直接返回 ; 如果没有找到 , 则继续遍历下一个 Element[] dexElements 元素 ;

代码语言:javascript复制
/*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 索引开始 , 向后遍历 , 其中就隐含着 Dex 文件搜索的顺序机制 ;

顺序机制 : 如果在第 2 个 Dex 文件 和 第 3 个 Dex 文件中都包含该 A.class 字节码文件 , 则只会返回第 2 个 Dex 文件中的类 , 遍历到此处就返回了 , 不会去遍历到低 3 个 Dex 文件 ;

利用上述顺序加载的机制 , 将修复包的 dex 文件放在前面 , 就可以屏蔽后面出现问题的类 ;

示例 :

在已经发布的应用中 , 有 3 个 Dex 文件 , 其中第 2 个 Dex 文件中的 A.class 字节码类中出现了崩溃问题 , 这里我们将修复好的 A.class 文件打包成 Dex 文件 , 将其插入到原来第 1 个和第 2 个 Dex 文件之间 , 这样在类加载器加载 A.class 类时 , 按照顺序先把修复的 Dex 文件加载到内存中 , 不再向后查找第 3 个 Dex 文件中出现崩溃的 A.class 类了 ;

热修复只是在前面插入一个修复好的 Dex 文件 , 不会删除出现问题的 Dex 文件 ;

第 1 个 Dex 文件一定不能出现问题 , 如果出现问题 , 就无法在该 Dex 文件之气前插入 Dex 文件 , 这个主 Dex 文件中有 Application , MainActivity 等相关重要组件 , 无法进行替换 ;

如果第 1 个 Dex 文件出现问题 , 只能发布新版本 ;

实际上热修复机制属于一种利用漏洞的机制 , Google 没有正面支持该功能 , iOS 中就关闭了热修复功能 ;

0 人点赞