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的继承关系

其中,他们的关系如下:

  • 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;@Overrideprotected 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问题;

更多相关文章

  1. Pycharm安装PyQt5的详细教程
  2. NPM 和webpack 的基础使用
  3. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
  4. 读取android手机流量信息
  5. android 使用html5作布局文件: webview跟javascript交互
  6. Android(安卓)多媒体扫描过程(Android(安卓)Media Scanner Proces
  7. android“设置”里的版本号
  8. Android开发环境搭建
  9. Android(安卓)Resource介绍和使用

随机推荐

  1. asynchttpclient post方法使用
  2. Android将bitmap保存到自定义路径
  3. android获取本机的IP地址和mac物理地址
  4. Android依赖库版本不一致
  5. JS怎样调用Android本地原生方法
  6. Android移植--启动篇
  7. android 保存bitmap到指定文件
  8. android 从webview中读取缓存的图片
  9. Android 工具类 —— 截屏
  10. Android studio真机调试安装失败,虚拟机正