android中的类加载器

来源:互联网 发布:简述数据库系统的特点 编辑:程序博客网 时间:2024/05/29 18:42

android的类加载机制跟Java类似,也是采用“双亲委托”机制。即用户在用自己定义的类加载器加载类时,会首先尝试调用该加载器的父加载器来进行加载,加载不成功再返回给子加载器来加载。同样,该父类加载器在加载时也会调用其父类加载器进行加载,也就是原加载器的爷爷加载器进行加载,不成功再往下返回。一层层往上调用,直到到达“引导加载器”,这个加载器是Dalvik实现的一部分,是用C而非用Java实现的,因为类加载器也是类,使用前也要进行加载,这就要求有一个加载器来实现类加载器的加载。这个流程可以从ClassLoader类的loadClass()方法看出来:

protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {        Class<?> clazz = findLoadedClass(className);        if (clazz == null) {            try {                clazz = parent.loadClass(className, false);            } catch (ClassNotFoundException e) {                // Don't want to see this.            }            if (clazz == null) {                clazz = findClass(className);            }        }        return clazz;}

如上述代码所示,在调用loadClass加载类时,会首先判断该类是否已经被加载,如果该类已经被加载则返回此类,否则会调用父类加载器来尝试加载。如果父类加载器无法加载,则会返回给子加载器来加载(findClass)。为什么会出现加载器无法加载的情况呢,是因为这些加载器类(引导加载器、系统加载器等)都有自己的“命名空间”,加载器在进行类加载时会从固定目录中尝试类加载,如果在这些目录中无法找到想加载的类加载就会失败。下面通过一个动态加载类的例子来捋一下流程:

String path = Environment.getExternalStorageDirectory() + "/";File file = getDir("dex", 0) ;String filename = "TestB.apk";classLoader = new DexClassLoader(path + filename, file.getAbsolutePath(),null, ClassLoader.getSystemClassLoader());Application app = classLoader.loadClass(strClassName).newInstance();

上述classLoader是DexClassLoader对象,loadClass返回一个被加载的类的Class对象,我们来分析一下loadClass到底是如何加载一个类的。DexClassLoader继承自BaseClassLoader,在DexClassLoader中并没有loadClass的实现,因为DexClassLoader继承自BaseClassLoader,BaseClassLoader又继承自ClassLoader,所以在加载时是调用了ClassLoader的loadClass。从上面代码中可以知道,loadClass其实是调用findClass来实现类的加载。因为被加载的类不在父类加载器的“命名空间”内,所以最后还是要调用DexClassLoader的findClass来实现类的加载。下面我们就来看一下findClass是如何实现类加载的。

protected Class<?> findClass(String name) throws ClassNotFoundException {        Class c = pathList.findClass(name);        if (c == null) {            throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);        }        return c;}

其中pathList为DexPathList对象,即调用DexPathList的findclass,所以我们需要继续跳转到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;}

其中,dex是DexFile对象,在这里我们来看一下DexFile类长啥样,DexFile也是在类加载中比较底层的起重要作用的类。

public final class DexFile {    private int mCookie;    private final String mFileName;    private final CloseGuard guard = CloseGuard.get();    public DexFile(String fileName) throws IOException {        mCookie = openDexFile(fileName, null, 0);        mFileName = fileName;        guard.open("close");    }}

在DexFile中有几个native方法,比较重要的两个是openDexFile、defineClass。openDexFile返回值类型为int(在DexFile的构造中将此返回值传给mCookie),源码中是这样注释的:A pointer to our internal data structure,即dex文件被加载之后的内存地址。而private static Class defineClass(String name, ClassLoader loader, int cookie)的注释是:Load a class from a DEX file,参数cookie就是openDexFile的返回值,而name是要加载的类。总的来说,可以这么理解:先用openDexFile将dex文件读到一个特殊的数据结构中(支持从硬盘或者内存中读取),然后用defineClass实现某一个类的加载。

0 0
原创粉丝点击