Android Class类加载机制

来源:互联网 发布:2017淘宝违禁词有哪些 编辑:程序博客网 时间:2024/06/06 02:15

1 前言

Class类加载机制是了解Android高级技术的基础,现在热门的热修复,插件化,以及Dex分包等技术,都需要理解class类加载机制,了解了class类加载机制我们能更好的理解Android内部原理,从而使我们的技术提高一个层次
Android是基于java的,我们先来了解一下java的类加载机制

2 java Class类加载机制

java系统自带有三个类加载器,分别是Bootstrap ClassLoader, Extention ClassLoader,Appclass Loader
Bootstrap ClassLoader :是用C++语言实现的,最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。
Extention ClassLoader :扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
Appclass Loader:也称为SystemAppClass 加载当前应用的classpath的所有类。

加载顺序
1. Bootstrap CLassloder
2. Extention ClassLoader
3. AppClassLoader

在java代码中我们可以使用如下代码进行测试:

ClassLoader classLoader = Main.class.getClassLoader();//是APPClassLoaderSystem.out.println("Main.class ClassLoader:" + classLoader.toString());URL[] urls = ((URLClassLoader)classLoader).getURLs();printURL(urls);ClassLoader parent1 = classLoader.getParent();//是ExtClassLoaderSystem.out.println("Main.class parent ClassLoader:" + parent1.toString());URL[] urls2 = ((URLClassLoader)parent1).getURLs();printURL(urls2);ClassLoader parent2 = parent1.getParent();//是BootstrapLoaderSystem.out.println("ExtClassLoader parent ClassLoader:" + parent2.toString());

这三者的关系如下:
这里写图片描述

这三者的继承关系如下:
这里写图片描述

3 Android Class类加载机制

在Android中 有 2种类加载器:
PathClassLoaderDexClassLoader。分别位于如下
libcore\dalvik\src\main\java\dalvik\system\PathClassLoader.java
libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java

定义如下

/** * Provides a simple {@link ClassLoader} implementation that operates on a list * of files and directories in the local file system, but does not attempt to * load classes from the network. Android uses this class for its system class * loader and for its application class loader(s). */public class **PathClassLoader** extends BaseDexClassLoader {    ......}/** * A class loader that loads classes from {@code .jar} and {@code .apk} files * containing a {@code classes.dex} entry. This can be used to execute code not * installed as part of an application. * * <p>This class loader requires an application-private, writable directory to * cache optimized classes. Use {@code Context.getCodeCacheDir()} to create * such a directory: <pre>   {@code *   File dexOutputDir = context.getCodeCacheDir(); * }</pre> * * <p><strong>Do not cache optimized classes on external storage.</strong> * External storage does not provide access controls necessary to protect your * application from code injection attacks. */public class **DexClassLoader** extends BaseDexClassLoader {    ......}

可以看到都继承了BaseDexClassLoader 这个基类,这个类又是继承ClassLoader的,这里先不去管它。我们来看注释。
对于PathClassLoader,从文档上的注释来看:Android是使用这个类作为其系统类和应用类的加载器。并且对于这个类呢,只能去加载已经安装到Android系统中的apk文件。
对于DexClassLoader,依然看下注释:可以看到可以加载从jar包中的,apk中的类。

Android要加载一个类 是通过ClassLoader的findClass方法 在dex中查找这个类 找到后加载到内存
我们来看下Android中类是如何加载的。
我们查看PathClassLoader与DexClassLoader均发现没有findClass()方法,因此我们在其父类BaseDexClassLoader中找一下

/** * Base class for common functionality between various dex-based * {@link ClassLoader} implementations. */public class BaseDexClassLoader extends ClassLoader {    private final DexPathList pathList;    ...    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();        Class c = pathList.findClass(name, suppressedExceptions);        if (c == null) {            ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);            for (Throwable t : suppressedExceptions) {                cnfe.addSuppressed(t);            }            throw cnfe;        }        return c;    }    ....}

可以看到,Android中类的加载是从DexPathList 的findClass()方法中加载的,我们再来看DexPathList 中查看

final class DexPathList {    private static final String DEX_SUFFIX = ".dex";    ......    /**     * List of dex/resource (class path) elements.     * Should be called pathElements, but the Facebook app uses reflection     * to modify 'dexElements' (http://b/7726934).     */    private final Element[] dexElements;    ......    /**     * Finds the named class in one of the dex files pointed at by     * this instance. This will find the one in the earliest listed     * path element. If the class is found but has not yet been     * defined, then this method will define it in the defining     * context that this instance was constructed with.     *     * @param name of class to find     * @param suppressed exceptions encountered whilst finding the class     * @return the named class or {@code null} if the class is not     * found in any of the dex files     */    public Class findClass(String name, List<Throwable> suppressed) {        for (Element element : dexElements) {            DexFile dex = element.dexFile;            if (dex != null) {                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);                if (clazz != null) {                    return clazz;                }            }        }        if (dexElementsSuppressedExceptions != null) {            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));        }        return null;    }}

可以看到查找类是在dexElements数组中依次遍历查找的。一个classloader可以包含多个dex,其中这个集合中的对象就是所有的dex文件,查找是从头开始遍历所有的dex 如果在dex中找到所需要的类,那么就直接返回。
在这个dex中查找相应名字的类,之后 defineClass把字节码交给虚拟机就完成了类的加载。

原创粉丝点击