【Android 热修复】热修复原理 ( 类加载分析 | 分析 PathClassLoader 源码 | 分析 BaseDexClassLoader 源码 | 分析 PathDexList 源码 )

2023-03-29 09:41:50 浏览数 (1)

文章目录

  • 一、分析 PathClassLoader 源码
  • 二、分析 BaseDexClassLoader 源码
  • 三、分析 PathDexList 源码
  • 四、 源码资源

一、分析 PathClassLoader 源码


PathClassLoader 是 Android 平台的类加载器 , 继承了 BaseDexClassLoader ;

代码语言:javascript复制
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
    
    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}

源码路径 : libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java

二、分析 BaseDexClassLoader 源码


BaseDexClassLoader 中的 findClass 方法 , 就是查找字节码类的核心方法 ;

BaseDexClassLoader 的 protected Class<?> findClass(String name) 是 protected 属性的 , 不能直接调用 , 需要通过反射才能调用 ;

应用调用所有的类的入口 , 就是该函数 , 所有 Java 类都是通过该类进行查找的 ;

BaseDexClassLoader 中的 findClass 方法分析 : 传入查找的类名 name 后 , 会调用 DexPathList pathList 成员的额 findClass 方法 ;

代码语言:javascript复制
public class BaseDexClassLoader extends ClassLoader {

    /**
     * Hook for customizing how dex files loads are reported.
     *
     * This enables the framework to monitor the use of dex files. The
     * goal is to simplify the mechanism for optimizing foreign dex files and
     * enable further optimizations of secondary dex files.
     *
     * The reporting happens only when new instances of BaseDexClassLoader
     * are constructed and will be active only after this field is set with
     * {@link BaseDexClassLoader#setReporter}.
     */
    /* @NonNull */ private static volatile Reporter reporter = null;

    private final DexPathList pathList;


    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
        // TODO We should support giving this a library search path maybe.
        super(parent);
        this.pathList = new DexPathList(this, dexFiles);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException(
                    "Didn't find class ""   name   "" on path: "   pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;
        }
        return c;
    }
}

源码路径 : libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

三、分析 PathDexList 源码


应用调用类 A.java 时 , 通过 PathClassLoader 调用 , PathClassLoader 没有实际内容 , 只是继承 BaseDexClassLoader , 主要靠 BaseDexClassLoader 的 findClass 方法查找 A.class 文件 , BaseDexClassLoader 又调用 DexPathList pathList 成员的 findClass 方法 , 查找 A.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

四、 源码资源


源码资源 :

  • GitHub 地址 : https://github.com/han1202012/HotFix
  • CSDN 源码快照 : https://download.csdn.net/download/han1202012/16651312

0 人点赞