11月更文挑战|Android基础-理解ClassLoader

1,127 阅读3分钟

这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

Java-ClassLoader

Java中类加载器主要分为两种类型:系统类加载器和自定义类加载器。

加载器类名介绍
Booststap ClassLoader (引导类加载器)C/C++编写没有Java类它是由C/C++编写的。主要是用来加载JDK核心代码库。例如java.lang、java.uti等系统类
Extensions ClassLoader (拓展类加载器)ExtClassLoader用于加载Java拓展类,提供系统类之外的额外功能
Application ClassLoader (应用程序加载器)AppClassLoader可以称为System ClassLoader 用于加载目录类库
Custom ClassLoader (自定义类加载器)继承ClassLoader自定义加载器是通过继承ClassLoader实现自定义功能类加载器

ClassLoader继承关系

ClassLoader继承关系如下所示,Java中编写带main()函数的类是继承自AppClassLoader;AppClassLoader的父加载器是ExtClassLoader。而ExtClassLoader的父加载器是Booststap ClassLoader,但由于是C/C++编写没有Java类;

classDiagram
      ClassLoader <|-- SecureClassLoader
      SecureClassLoader <|-- URLClassLoader
      URLClassLoader <|-- ExtClassLoader
      URLClassLoader <|-- AppClassLoader

加载过程-双亲委托模式

类加载器查找类采用双亲委托模式,就是首先判断该类是否已经载入:如果没有载入就委托父加载器进行查找,进行递归操作直到委托到最顶层Booststap ClassLoader,如果查找到了就直接返回,没有找到依次向下查找,若找不到最后交给自己去查找。

具体原理步骤

  1. 一个类加载器收到类加载请求,首先会把请求委托给父类加载器执行。
  2. 如果父类还存在父类接在其,则继续向上委托,直到顶层Booststap ClassLoader为止。
  3. 如果父类加载器可以加载就返回结果,如果父类加载失败则由子类自己尝试加载,子类加载失败抛出ClassNotFoundException异常。

Android-ClassLoader

Java中的ClassLoader可以加载jar包和Class文件,而在Android中并不支持在Android虚拟机中加载的是dex文件。因此对于Android的ClassLoader并不能和Java的ClassLoader相通。而Android的ClassLoader分类类型其实和Java类似,同样也是系统类加载器和自定义类加载器两种。

加载器类名介绍
BootClassLoaderBootClassLoaderBootClassLoader是Android系统用来加载常用类的加载器,不同点在于它不是用C/C++代码实现,而是Java代码。
DexClassLoaderDexClassLoader用于加载dex文件以及含有dex文件的压缩包
PathClassLoaderPathClassLoaderPathClassLoader用来加载系统类和应用程序类

ClassLoader继承关系

classDiagram
      ClassLoader <|-- BootClassLoader
      ClassLoader <|-- BaseDexClassLoader
      ClassLoader <|-- SecureClassLoader
      SecureClassLoader <|-- URLClassLoader
      BaseDexClassLoader <|-- InMemoryDexClassLoader
      BaseDexClassLoader <|-- PathClassLoader
      BaseDexClassLoader <|-- DexClassLoader

加载过程

AndroidClassLoader同样也是双亲委托模式。 ClassLoader使用loadClass方法加载类loadClass调用findClass方法然后调用到DexPathListfindClass方法;遍历加载过的所有dex文件,再调用loadClassBinaryName方法去加载需要加载的类;loadClassBinaryName调用Native方法defineClass加载类。最后ClassLoader创建和加载过程就完成了。

    // #ClassLoader.loadClass
  public Class<?> loadClass(String className) throws ClassNotFoundException {
        return loadClass(className, false);
    }

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(className);
        if (clazz == null) {
            ClassNotFoundException suppressed = null;
            try {
                clazz = parent.loadClass(className, false);
            } catch (ClassNotFoundException e) {
                suppressed = e;
            }

            if (clazz == null) {
                try {
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }
        return clazz;
    }
    // #ClassLoader.findClass
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = pathList.findClass(name);
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }
        return clazz;
    }
     // #DexPathList.findClass
    public Class findClass(String name) {
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;
            if (dex != null) {
                Class clazz = dex.loadClassBinaryName(name, definingContext);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        return null;
    }
    // #DexPathList.loadClassBinaryName
    public Class loadClassBinaryName(String name, ClassLoader loader) {
        return defineClass(name, loader, mCookie);
    }
    private native static Class defineClass(String name, ClassLoader loader, int cookie);

参考