Parent Delegation Model(双亲委托模型)
1 | //classloader构造方法,子classloader持有父classloader |
classloader对class的加载过程:
- 源 ClassLoader 先判断该 Class 是否已加载,如果已加载,则直接返回 Class,如果没有则委托给父类加载器。
- 父类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则委托给祖父类加载器。
- 依此类推,直到始祖类加载器(引用类加载器)。
- 始祖类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的子类加载器。
- 始祖类加载器的子类加载器尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的孙类加载器。
- 依此类推,直到源 ClassLoader。
- 源 ClassLoader 尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,源 ClassLoader 不会再委托其子类加载器,而是抛出异常。
Android对class的装载
当 Android 系统安装一个应用的时候,会针对不同平台对 Dex 进行优化,这个过程由一个专门的工具来处理,叫 DexOpt 。DexOpt 是在第一次加载 Dex 文件的时候执行的,该过程会生成一个 ODEX 文件,即 Optimised Dex。执行 ODEX 的效率会比直接执行 Dex 文件的效率要高很多,加快 App 的启动和响应。
总之,Android 中的 Dalvik/ART 无法像 JVM 那样 直接 加载 class 文件和 jar 文件中的 class,需要通过 dx 工具来优化转换成 Dalvik byte code 才行,只能通过 dex 或者 包含 dex 的jar、apk 文件来加载(注意 odex 文件后缀可能是 .dex 或 .odex,也属于 dex 文件),因此 Android 中的 ClassLoader 工作就交给了 BaseDexClassLoader 来处理。
关于BaseDexClassLoader及其子类
BaseDexClassLoader是ClassLoader的具体实现,BaseDexClassLoader 的子类是 PathClassLoader 和 DexClassLoader
PathClassLoader
1 | //dexPath 包含 dex 的 jar 文件或 apk 文件的路径集,多个以文件分隔符分隔,默认是“:”,具体是已经安装的APK文件路径,应该是app默认的classLoader |
PathClassLoader是app默认的classloader,只能加载已安装的app内的class。(所以对于插件化开发意义不大,除非是用来在原有的APP开发基础上搞事情)
DexClassLoader
- A class loader that loads classes from .jar and .apk filescontaining a classes.dex entry. This can be used to execute code notinstalled as part of an application.
官方描述可以看到这个classloader适合用于从外部加载其他库类的class。可以随意从SD卡或内存空间加载包含class.dex的.jar或.apk文件
1 | //String dexPath : 包含 class.dex 的 apk、jar 文件路径 ,多个用文件分隔符(默认是 :)分隔 |
String optimizedDirectory : 用来缓存优化的 dex 文件的路径,即从 apk 或 jar 文件中提取出来的 dex 文件。该路径不可以为空,且应该是应用私有的,有读写权限的路径(实际上也可以使用外部存储空间,但是这样的话就存在代码注入的风险),可以通过以下方式来创建一个这样的路径:
1 | //above API 21 |
BaseDexClassLoader的实现
1 | static class Element { |
将pathList转换为List,通过loadDexFile()方法JNI加载具体dexFile。
加载具体class:dex.loadClassBinaryName()根据dex的名称从List中按顺序查找并加载对应的class.因此对于同名的class来说,所在dex越前,则会先被加载到
至此,BaseDexClassLader 寻找 class 的路线就清晰了:
- 当传入一个完整的类名,调用 BaseDexClassLader 的 findClass(String name) 方法
- BaseDexClassLader 的 findClass 方法会交给 DexPathList 的 findClass(String name, List suppressed 方法处理
- 在 DexPathList 方法的内部,会遍历 dexFile ,通过 DexFile 的 dex.loadClassBinaryName(name, definingContext, suppressed) 来完成类的加载