Android中的ClassLoader

举报
月色很美 发表于 2018/12/20 19:19:44 2018/12/20
【摘要】 Android中的ClassLoaderJava中的ClassLoader可以加载jar包和class文件,在Android则不同。在Android中,不论DVM还是ART,加载的都是dex文件。Android中的ClassLoader可以分为系统类加载器和自定义加载器。Android中ClassLoader类继承关系如下BootClassLoader源码BootClassLoader是Cl...

Android中的ClassLoader

Java中的ClassLoader可以加载jar包和class文件,在Android则不同。在Android中,不论DVM还是ART,加载的都是dex文件。Android中的ClassLoader可以分为系统类加载器和自定义加载器。

Android中ClassLoader类继承关系如下

BootClassLoader

源码

BootClassLoaderClassLoader的一个内部类,并继承自ClassLoader

它是包内可见的,因此我们没法使用它,也不能使用动态加载。

UrlClassLoader

UrlClassLoader继承自SecureClassLoader,它只能用于加载jar文件,但是由于 dalvik 不能直接识别jar,所以在 Android 中无法使用这个加载器。

BaseDexClassLoader

源码

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);

    if (reporter != null) {
        reportClassLoaderChain();
    }
}
 public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
    super(parent);
    this.pathList = new DexPathList(this, dexFiles);
}


BaseDexClassLoader继承自ClassLoader,用于加载各种dex中的类。

具体参数的含义

  • dexPath. 指目标类所在的APK或jar文件的路径,类装载器将从该路径中寻找指定的目标类,该类必须是APK或jar的全路径.如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得。最终做的是将dexPath路径上的文件ODEX优化到内部位置optimizedDirectory,然后,再进行加载的。

  • optimizedDirectory. 由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径。这也是对apk中dex根据平台进行ODEX优化的过程。其实APK是一个程序压缩包,里面包含dex文件,ODEX优化就是把包里面的执行程序提取出来,就变成ODEX文件,因为你提取出来了,系统第一次启动的时候就不用去解压程序压缩包的程序,少了一个解压的过程。这样的话系统启动就加快了。为什么说是第一次呢?是因为DEX版本的也只有第一次会解压执行程序到 /data/dalvik-cache(针对PathClassLoader)或者optimizedDirectory(针对DexClassLoader)目录,之后也是直接读取目录下的的dex文件,所以第二次启动就和正常的差不多了。当然这只是简单的理解,实际生成的ODEX还有一定的优化作用。ClassLoader只能加载内部存储路径中的dex文件,所以这个路径必须为内部路径。

  • librarySearchPath.指目标类中所使用的C/C++库存放的路径

  • parent. 是指该装载器的父装载器,一般为当前执行类的装载器,例如在Android中以context.getClassLoader()作为父装载器。

PathClassLoader

源码

public PathClassLoader(String dexPath, ClassLoader parent) {
    super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
    super(dexPath, null, librarySearchPath, parent);
}


PathClassLoader的构造方法中没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的值为/data/dalvik-cache目录。所以PathClassLoader无法定义解压的dex文件的存储路径,因此PathClassLoader通常用来加载已经安装的apk的dex文件。

DexClassLoader

源码

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


DexClassLoader可以加载dex文件以及包含dex的压缩文件,不管加载哪种文件,最终都是要加载dex文件。

InMemoryDexClassLoader

源码

public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) {
    super(dexBuffers, parent);
}

public InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent) {
    this(new ByteBuffer[] { dexBuffer }, parent);
}

InMemoryDexClassLoader是API26的时候新增的。ByteBuffer数组构造了一个DexPathList,可用于内存中的dex文件。

ClassLoader 的双亲委托模型

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException{
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                c = findClass(name);
            }
        }
        return c;
}
private Class<?> findBootstrapClassOrNull(String name){
    return null;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}


加载类的时候,首先判断有没有加载过,如果已经加载过了直接返回,否则就判断父加载器是否存在。如果存在父加载器,则调用父加载器的loadClass方法,不存在则调用findBootstrapClassOrNull方法。但是findBootstrapClassOrNull会直接返回null。所以最终又会调用到findClass方法。而findClass会直接抛出异常,所以这个需要子类来实现。这就是双亲委托模型。双亲委托模型一方面可以避免重复加载类,另一方面可以避免有人恶意编写一个类加载到JVM中。

Refer:https://www.jianshu.com/p/a620e368389a


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。