双亲委派模型


一、Android类加载器

虚拟机加载类时,将二进制字节流读取到内存,可以从class文件、zip包、jar包、网络或动态代理生成二进制字节流。

某一个类,需要保证在虚拟机中的唯一性,由类加载器和他本身决定,不同的类加载器加载的类肯定不等,相同的类加载器保证某个类只加载一次。

Android类加载器
PathClassLoader,DexClassLoader,BootClassLoader。

Android类加载器继承关系

ClassLoader,抽象类,定义了类加载的基础方法loadClass,findClass。
BootClassLoader,加载系统Sdk框架类,系统启动时创建,如Context,Activity类。

Activity.class.getClassLoader();
String.class.getClassLoader();

输出结果是BootClassLoader,调用Class类的getClassLoader方法,这些系统类的加载器都是BootClassLoader。

BaseDexClassLoader类,父类,路径都在dalvik.system包中。
PathClassLoader,应用启动时创建一个实例,加载系统内已安装apk中的类。

MainActivity.class.getClassLoader();

MainActivity.class.getClassLoader(),输出结果PathClassLoader,自定义类的类加载器。
Context类内部的getClassLoader加载器,PathClassLoader。
DexClassLoader,加载未安装apk中的类,dex文件,在热修复/插件化时可以使用,加载外部存储路径下的apk或dex补丁。


二、双亲委派机制

类加载时默认采用双亲委派机制,当类加载器收到加载任务,总是先将任务委派给内部的父类加载器,(内部包含父类加载器),直到将请求传递到最顶层的启动类加载器,如果成功加载,返回通知结果,如果失败则由下层加载。

父加载器

只有当父类无法完成加载请求时,子加载器加载。
BootClassLoader是DexClassLoader和PathClassLoader的父加载器。

1,原理

ClassLoader的loadClass()方法。

protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
    //先检查是否已加载。
    Class c = findLoadedClass(name);
    if (c == null) {
        //父加载器先找
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
        }
        //子类重写查找
        if (c == null) {
            c = findClass(name);
        }
    }
    return c;
}

判断是否已加载class,未加载,先parent去加载,递归调用loadClass(),
如果parent是空,是顶层BootClassLoader,重写loadClass(),直接查找findClass()。
如果parent加载不成功,findClass()自己加载。
BaseDexClassLoader,重写findClass()。
自定义ClassLoader,同上,重写findClass(),不需要改变loadClass的基础逻辑。

loadClass(),优先委派父加载器,当父加载器不能成功加载时,调用自己重写当findClass()自己加载。

BootClassLoader重写的loadClass方法。

@Override
protected Class loadClass(String className, boolean resolve)
        throws ClassNotFoundException {
    Class clazz = findLoadedClass(className);
    if (clazz == null) {
        clazz = findClass(className);
    }
    return clazz;
}

不需要请求父加载,本身就在顶层,直接findClass(),BootClassLoader重写的findClass()方法。

@Override
protected Class findClass(String name) throws ClassNotFoundException {
    return Class.classForName(name, false, null);
}

通过Class类的classForName方法加载。ClassLoader类的findClass()是空方法,需要子类实现。

2,BaseDexClassLoader构造参数

dexPath,dex路径,DexClassLoader是dex文件或jar包的路径,可以多个,PathClassLoader类是apk的安装路径。
optimizedDirectory,dex文件被加载后会被编译器优化,优化之后的dex存放路径,经过odex优化过的将apk压缩包里的dex提取出来变成odex文件,这样第一次启动就不用解压缩包。当加载app时,已经将apk安装在本地文件系统,并且内部dex应被提取并执行过优化。
当PathClassLoader加载时,会将其解压到固定的/data/dalvik-cache目录,所以,不需要指定该目录,是空。
当DexClassLoader加载时,非apk安装的dex文件,将dex优化解压到该目录下,必须是应用私有的可写路径,且不能是空,防止App被注入攻击,可以将热修复包或动态插件下载到私有路径中,自定义继承DexClassLoader加载器指定下载路径。
ClassLoader parent,app启动时,将系统创建的BootClassLoader传入,它是PathClassLoader类加载器的父加载器。

PathClassLoader和DexClassLoader区别在于是否指定了optimizedDirectory,PathClassLoader不指定。
dexpath目前只支持.dex、.jar、.apk、.zip格式。

dexElements,DexPathList的Element数组,根据dexPath路径,(通过:连接多个dex或apk路径)保存分割后的对应Flie创建的Elememt,由File和DexFile组成,DexFile使用native方法openDexFile打开了具体的file并输出到优化路径。

BaseDexClassLoader的findClass()方法。

@Override
protected Class findClass(String name) throws ClassNotFoundException {
    List suppressedExceptions = new ArrayList();
    Class c = pathList.findClass(name, suppressedExceptions);
    if (c == null) {
        ClassNotFoundException cnfe = new ClassNotFoundException(
                "Didn't find class \"" + name + "\" on path: " + pathList);
        ...
    }
    return c;
}

DexPathList类,分离的每一个dex加载路径存储Element,数组,加载时,遍历Elements数组。

3,双亲委派机制的作用

共享功能,一些framework层级的类一旦被顶层加载器加载,缓存在内存。在其他任何地方用到时,都遵守双亲加载机制,派发到顶层加载器,因已经加载,所以都不需要重新加载。
隔离功能,保证核心类库的纯净和安全,防止恶意加载。

双亲委派机制在很大程度上防止内存中出现多个相同的字节码文件,加载类的时候默认会使用当前类的ClassLoader进行加载,只有当你使用该class的时候才会去装载,一个加载器只会装载同一个class一次。可以通过A.class.getClassLoader查看当前A类的加载器。


任重而道远

你可能感兴趣的:(双亲委派模型)