# 未完

# Java 中的 ClassLoader

在 Java 中,类加载器的作用是通过一个类的全限定名获取描述这个类的二进制字节流,然后加载到虚拟机中生成类对象并提供引用。
对 Java 虚拟机来说,类加载器可以分为 2 中:

  • 启动类加载器。由 C++ 代码实现,是 Java 虚拟机的一部分
  • 其他加载器。由 Java 代码实现,独立于虚拟机,并且全都继承自 java.lang.ClassLoader 这个类。

对于我们开发人员来说,类加载器由 3 种:

  • bootstrap ClassLoader
  • Extension ClassLoader 加载指定扩展包中的类
  • Application ClassLoader 加载 ClassPath 指定路径上的类

# 双亲委派模型

双亲委派模型是一种类加载的模型,是被推荐使用的类加载模型。双亲委派模型的定义是每次去加载一个类时,先判断这个类是否加载过,如果没有加载过,那么判断有没有父类加载器,如果有父类加载器,那么先调用父类加载器的加载方法;如果父类加载器加载失败,那么就调用当前类加载器。

使用双亲委派模型的好处:
使 Java 类与 Java 类加载器一起产生了优先级层次,保证了同一个类始终由一个类加载器去加载。

双亲委派模型的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

protected synchronized Class<?> loadClass(Stirng name,boolean resolve) throws ClassNotFoundException {
Class c = findLoadedClass(name);
if(c == null){
try{
if(parent != null){
//父类加载器存在,继续调用父类加载器的loadClass方法
c = parent.loadClass(name,false);
}else{
//父类加载器不存在,调用启动类加载器去加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e){

}
//如果父类加载器和启动类加载器都加载失败,使用当前类加载器去加载
if ( c == null ){
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}

# Android Dalvik 虚拟机中的类加载器

# BaseDexClassLoader

一个基于 Dex 的通用功能的 ClassLoader 实现,其核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

//libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
Class BaseDexClassLoader extends ClassLoader{

private final DexPathList pathList;
//一个ClassLoader数组,用来加载pathList pathList中可能依赖的类和资源,用来实现
//Android中的AndroidManifest.xml中<use-library>标签中的库
protected final ClassLoader[] sharedLibraryLoaders;
protected final ClassLoader[] sharedLibraryLoadersAfter;

public BaseDexClassLoader(String dexPath,String librarySearchPath,
ClassLoader parent,ClassLoader[] sharedLibraryLoaders,
ClassLoader[] sharedLibraryLoadersAfter,boolean isTrust){
super(parent);
...
this.pathList = new DexPathList(this,dexPath,librarySearchPath,null,isTrust);


}


protected synchronized Class<?> findClass(Stirng name,boolean resolve) throws ClassNotFoundException {

if(sharedLibraryLoaders != null){
for(ClassLoader loader : sharedLibraryLoaders){
try{
return loader.loadClass(name);
}catch(ClassNotFoundException ignore){
}
}
}
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = dexPath.findClass(name,suppressedExceptions);
if(c == null){
return c;
}
if(sharedLibraryLoadersAfter != null){
for(ClassLoader loader : sharedLibraryLoadersAfter){
try{
return loader.loadClass(name);
}catch(ClassNotFoundException ignore){
}
}
}
if(c == null){
throw new ClassNotFoundException();
}
return c;
}
}

从上面可以看出,BaseDexClassLoader 并没有遵守 Java 的双亲委托模型。

BaseDexClassLoader 将代码加载又委托给了 DexPathList 来进行加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

//libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
class DexPathList{
Element[] dexElements;
public DexPathList(...){
...
dexElement = makePathElements(spliteDexPath(dexPath)
,null,null,classLoader, isTrust);

...
}


//这里有关于热修复实现的知识点,就是将补丁 dex 文件放到 dexElements 数组靠前位置,
//这样在加载 class 时,优先找到补丁包中的 dex 文件,加载到 class 之后就不再寻找,从
//而原来的 apk 文件中同名的类就不会再使用,从而达到修复的目的

public Class<?> findClass(String name,List<Throwable> suppressed){
for(Element element : dexElements){
Class<?> clazz = element.findClass(name,classLoader,suppressed);
if(clazz != null){
return clazz;
}
}
return null;
}

public static Element[] makeDexElements(List<File> files,File optimizedDictionary,List<IOException> suppressedExceptions,ClassLoader classloader,boolean isTrusted){
Element[] elements = new Element[files.size()];

for(Files file : files){
if(file.isDictionary()){
elements[elementsPos++] = new Element(file);
}else{
if(file.isFile()){
DexFile dex = null;
if(file.path.endWith(".dex")){
dex = loadDexFile(file,optimizedDictionary,loader,elements);
if(dex != null){
elements[elementsPos++] = new Element(dex,null);
}
}else{
dex = loadDexFile(file,optimizedDictionary,loader,elements);
if(dex == null){
elements[elementsPos++] = new Element(file);
}else{
elements[elementsPos++] = new Element(dex,file);
}
}
}
}
}
return elements;
}


private static DexFile loadDexPath(File file,File optimizedDictionary,ClassLoader loader,Element[] elements){
if(optimizeDictory == null){
return new DexFile(file,loader,elements);
}else{
//创建一个文件,路径为optimizedDictionary指定文件夹内,file同名的文件。
String optimizedPath = optimizedPathFor(file,optimizedDictionary);
...
}
}
}




//libcore/dalvik/src/main/java/dalvik/system/DexFile.java

final Class DexFile{
private Object mCookie;

public DexFile(File file,ClassLoader loader,DexPathList.Element[] elements){
mCookie = openDexFile(...);
}


private static Object openDexFile(){
return openDexFileNative(...);
}
}

# PathClassLoader

只能加载已经安装过的 Apk

# DexClassLoader

可以加载 jar,apk,dex, 可以直接从 SD 卡加载未安装的 apk