Android ClassLoader解析(2) - Android ClassLoader

ClassLoader的主要作用就是加载类,且Android应用逻辑也是使用Java语言编写的,但Android ClassLoader和Java ClassLoader的原理却不太一样,关于Java 中ClassLoader的主要原理及使用见上一篇博文,本文主要比较两者的差别后,主要讨论Android ClassLoader的原理及使用。

Android ClassLoader & Java ClassLoader

Java中的ClassLoader可以加载jar文件和Class文件;但在Android中不同,Android打包后是apk应用,解压后看到其中包含一个class.dex文件,无论DVM还是ART他们加载的不再是Class文件,而是Dex文件,因此ClassLoader在加载类的过程中也略有不同。

ClassLoader的继承关系

Android ClassLoader解析(2) - Android ClassLoader_第1张图片

其中,他们的关系如下:

  • ClassLoader:抽象类,其中定义了ClassLoader的主要功能。BootClassLoader是它的内部类;
  • SecureClassLoader: 和JDK8中的SecureClassLoader类的代码是一样的,它继承了抽象类ClassLoader。SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性;
  • URLClassLoader:和JDK8中的URLClassLoader类的代码是一样的,它继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源;但由于dalvik不能直接识别jar,所以在Android中无法使用这个加载器;
  • BaseDexClassLoader:继承自ClassLoader,是抽象类ClassLoader的具体实现类,PathClassLoader和DexClassLoader都继承它,他们的主要逻辑都在这里;
  • InMemoryDexClassLoader:是Android8.0新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件;

BootClassLoader

该类在libcore/ojluni/src/main/java/java/lang/ClassLoader.java,与Java中的BootstrapClassLoader不同,他不是C/C++代码,而是Java代码,且是ClassLoader的内部类,且BootClassLoader是在Zygote进程的入口方法中创建的。(注:BootClassLoader的访问修饰符是默认的,只有在同一个包中才可以访问,因此我们无法使用)

PathClassLoader

该类在libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java,用来加载系统类和应用程序的类,且不建议开发直接使用。代码如下:

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);
    }
}

PathClassLoader则是在Zygote进程创建SystemServer进程时创建的,其构造方法有三个参数:

  • dexPath: 目标类所在的APK或jar文件的路径,多个路径用文件分隔符分隔,默认文件分隔符为‘:’。支持加载APK、DEX和JAR,也可以从SD卡进行加载,最后都会生成一个对应的dex文件。且最终将dexPath路径上的文件ODEX优化到内部位置optimizedDirectory,再进行加载;
  • librarySearchPath: 包含 C/C++ 库的路径集合,多个路径用文件分隔符分隔分割,可以为null;
  • parent: ClassLoader的parent;

DexClassLoader

该类在libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java,可以加载dex文件以及包含dex的apk文件或jar文件,也支持从SD卡进行加载。因此可以在应用未安装的情况下加载dex相关文件。因此,它是热修复和插件化技术的基础。代码如下:

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

其中,参数optimizedDirectory表示存储ODEX文件的路径,ClassLoader只能加载内部存储路径中的dex文件,所以这个路径必须是一个内部存储路径。 应用程序第一次被加载的时候,为了提高以后的启动速度和执行效率,Android系统会对dex相关文件做一定程度的优化,并生成一个ODEX文件,此后再运行这个应用程序的时候,只要加载优化过的ODEX文件就行了,省去了每次都要优化的时间。

PathClassLoader没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的路径为:/data/dalvik-cache

ClassLoader类加载过程

阅读源码可知,Android中ClassLoader加载类的过程也符合双亲委派机制。根据之前的分析可知,在查到对象的过程中,如果一直委托到顶层的父加载器依然找不到,则会调用findClass向下查找:

    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;
    }

且PathClassLoader和DexClassLoader的实现中只有构造函数,其具体实现都在BaseDexClassLoader中,其部分源码为:

private final DexPathList pathList;

@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;
}

DexFile 中的loadClassBinaryName:

public Class loadClassBinaryName(String name, ClassLoader loader) { 
    return defineClass(name, loader, mCookie);
}

private native static Class defineClass(String name, ClassLoader loader, int cookie);

因此,可得BaseDexClassLoader中有个pathList对象,pathList中包含一个DexFile的数组dexElements,类加载的过程即:遍历这个集合,通过DexFile去寻找,最终调用native方法的defineClass。

ClassLoader实例有多个

一个运行的Android应用至少有2个ClassLoader

  1. BootClassLoader:在Android系统启动的时候创建,用于加载系统Framework层级需要的类,而我们的Android应用也有可能需要用到一些系统的类,所以APP启动的时候也会将BootClassLoader传进来;
  2. 自己的ClassLoader实例:应用启动时创建,用于加载应用dex文件中的类;

Android ClassLoader动态加载可能遇到的问题

  1. 动态加载四大组件,需要提前在Manifest中注册;
  2. Resource资源问题,常见的是资源id问题;

你可能感兴趣的:(Android ClassLoader解析(2) - Android ClassLoader)