一、Android类加载器
虚拟机加载类时,将二进制字节流读取到内存,可以从class文件、zip包、jar包、网络或动态代理生成二进制字节流。
某一个类,需要保证在虚拟机中的唯一性,由类加载器和他本身决定,不同的类加载器加载的类肯定不等,相同的类加载器保证某个类只加载一次。
Android类加载器
PathClassLoader,DexClassLoader,BootClassLoader。

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<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);
...
}
return c;
}
DexPathList类,分离的每一个dex加载路径存储Element,数组,加载时,遍历Elements数组。
3,双亲委派机制的作用
共享功能,一些framework层级的类一旦被顶层加载器加载,缓存在内存。在其他任何地方用到时,都遵守双亲加载机制,派发到顶层加载器,因已经加载,所以都不需要重新加载。
隔离功能,保证核心类库的纯净和安全,防止恶意加载。
双亲委派机制在很大程度上防止内存中出现多个相同的字节码文件,加载类的时候默认会使用当前类的ClassLoader进行加载,只有当你使用该class的时候才会去装载,一个加载器只会装载同一个class一次。可以通过A.class.getClassLoader查看当前A类的加载器。
任重而道远
网友评论