ClassLoader ,了解一下

本文介绍了Android中的类加载机制,详细解析了ClassLoader、BootClassLoader、BaseDexClassLoader等关键类的作用及其实现方式,并探讨了双亲委托机制的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ClassLoader 类图:

图片来自文末参考文章中.jpg

Android中ClassLoader的介绍

  1. ClassLoader

    介绍:

    ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。包括类加载,验证,卸载等

    构造方法:

    public abstract class ClassLoader {
        private ClassLoader parent;
    
        protected ClassLoader() {
            this(getSystemClassLoader(), false);
        }
    
        protected ClassLoader(ClassLoader parentLoader) {
            this(parentLoader, false);
        }
    
        ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
            if (parentLoader == null && !nullAllowed) {
                //父类的类加载器为空,则抛出异常
                throw new NullPointerException("parentLoader == null && !nullAllowed");
            }
            parent = parentLoader;
        }
    }
    
  2. BootClassLoader

    介绍:

    • 父类构造器

    • BootClassLoader是ClassLoader的内部类,是单例类,包内可见,我们没法调用,也不能使用它来动态加载。Android系统启动时会使用BootClassLoader来预加载常用类。

    构造方法:

    class BootClassLoader extends ClassLoader {
        private static BootClassLoader instance;
    
        public static synchronized BootClassLoader getInstance() {
            if (instance == null) {
                instance = new BootClassLoader();
            }
    
            return instance;
        }
    
        public BootClassLoader() {
            super(null, true);
        } 
    }
    
  3. BaseDexClassLoader

    介绍:

    • BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类

    • PathClassLoader和DexClassLoader都继承它

    • 在它内部会初始化DexPathList对象,DexPathList这个对象中存储了dexElements(记录所有的dexFile文件)和nativeLibraryPathElements(记录所有的Native动态库, 包括app目录的native库和系统目录的native库)

    构造方法:

    public class BaseDexClassLoader extends ClassLoader {
        private final DexPathList pathList;
    
        public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
            super(parent);
            this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }
    }   
    

    参数:

    • dexPath: 包含目标类或资源的apk/jar列表;当有多个路径则采用:分割;

    • optimizedDirectory: 优化后的dex文件存在的目录, 可以为null;

    • libraryPath: native库所在路径列表;当有多个路径则采用:分割;

    • ClassLoader:父类的类加载器.

  4. PathClassLoader

    介绍:

    • 继承于BaseDexClassLoader. 只是封装了一下构造函数,没有复写父类的任何方法,所以具体的实现都在BaseDexClassLoader中

    • 主要用于系统和app的类加载器,其中optimizedDirectory为null, 采用默认目录/data/dalvik-cache/

    • dexPath 比较受限制,一般是已经安装应用的apk文件路径。在Android中,App安装到手机后,apk里面的classes.dex中的class均是通过PathClassLoader来加载的

    • BootClassLoader 是 PathClassLoader 的父加载器

    构造方法:

    public class PathClassLoader extends BaseDexClassLoader {
    
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
        }
    
        public PathClassLoader(String dexPath, String libraryPath,
                ClassLoader parent) {
            super(dexPath, null, libraryPath, parent);
        }
    }
    
  5. DexClassLoader

介绍:

  • 封装了BaseDexClassLoader对象,并没有覆写父类的任何方法

  • 可以从包含classes.dex的jar或者apk中,加载类的类加载器, 可用于执行动态加载

  • 构造方法中,参数optimizedDirectory用来缓存优化的dex文件的路径,即从apk或jar文件中提取出来的 dex 文件。该路径不可以为空,且应该是应用私有的,有读写权限的路径

构造方法:

    public class DexClassLoader extends BaseDexClassLoader {

        public DexClassLoader(String dexPath, String optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), libraryPath, parent);
        }
    }

双亲委托机制

类加载器查找Class所采用的是双亲委托模式,所谓双亲委托模式就是首先判断该Class是否已经加载,如果没有则不是自身去查找而是委托给父加载器进行查找,这样依次的进行递归,直到委托到最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该Class,就会直接返回,如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去查找。

具体委托过程如下:

  1. 源 ClassLoader 先判断该 Class 是否已加载,如果已加载,则直接返回 Class,如果没有则委托给父类加载器。

  2. 父类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则委托给祖父类加载器。

  3. 依此类推,直到始祖类加载器(引用类加载器)。

  4. 始祖类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的子类加载器。

  5. 始祖类加载器的子类加载器尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的孙类加载器。

  6. 依此类推,直到源 ClassLoader。

  7. 源 ClassLoader 尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,源 ClassLoader 不会再委托其子类加载器,而是抛出异常。

类加载

  1. loadClass(className) 调用加载class的方法。

    在loadClass()内部会先判断是否已经加载过该class,如果没有加载过就调用父类的loadClass(),如果父类中没有加载到就调用自己的findClass(className)方法去自己的路径中加载该class

  2. findClass(className)

    1. findClass()方法内部中是调用pathList(pathList是在BaseDexClassLoader构造方法中初始化的DexPathList对象)的findClass(name…)

    2. 即调用DexPathList内部的findClass(name) 。在该方法中会遍历dexEldments中的dex文件,然后调用dex.loadClassBinaryName方法去查找该类。

    3. 即调用DexFile内部的,loadClassBinaryName方法,在该方法中调用defineClass()方法

    4. defineClassNative()这是native方法,在该native方法中,去找对应的类

loadClass源码流程参考

从源码分析 Android dexClassLoader 加载机制原理

ClassLoader在热修复中的应用

在前面类加载过程中分析说了,类加载机制是双亲委托机制,而且在loadClass过程中会遍历pathList(dexElements)中的dex文件,所以当在前面的dex文件中找到类就直接返回了,而不会再进行查找,所以可以通过将修复的dex插到dexElements前面即可。思路是这样的,具体的操作会复杂的多。

ClassLoader动态加载Demo

  1. 生成jar包my.jar

    package com.thc.myjar;
    
    public class Plugin {
    
        public String getPluginStr() {
            return "恭喜你拿到了插件中的内容";
        }
    
        public int addInPulgin(int a, int b) {
            return a + b;
        }
    
    }
    
  2. 使用dx.jar将jar包转换成dex化之后的jar包

    dx --dex --output=mydex.jar my.jar
    
  3. push到或者代码复制到sdcard目录,然后通过DexClassLoader来进行加载

    private void loadJar() {
        /**
         * dexPath : dexPath
         *
         */
        String absolutePath = getCacheDir().getAbsolutePath();
        DexClassLoader dexClassLoader = new DexClassLoader(
                "/data/data/com.thc.pluginsample/cache/" + jarName,
                absolutePath,
                null,
                getClassLoader());
        try {
            Class<?> aClass = dexClassLoader.loadClass("com.thc.myjar.Plugin");
            Object o = aClass.newInstance();
            Method getPluginStr = aClass.getMethod("getPluginStr");
            String msg = (String) getPluginStr.invoke(o);
            Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

动态加载Jar Demo地址

感谢巨人的肩膀:

gityuan

热修复入门:Android 中的 ClassLoader

ClassLoader

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值