一、前言
随着 Android 技术的不断发展,对其内部机制的探索也日益深入。类加载机制作为 Android 运行时环境的核心组成部分之一,影响着应用的性能、安全性以及可扩展性。通过对 Android 类加载机制的研究,开发者可以更好地优化代码结构、提高应用的启动速度、解决潜在的安全问题,并实现更加灵活的功能扩展。
本文旨在对 Android 类加载机制进行简要介绍,帮助读者建立起对这一重要概念的初步认识,为进一步深入学习和实践 Android 开发奠定基础。
二、Android 类加载器的类型
2.1 BootClassLoader
- 这是 Android 系统启动时使用的类加载器。它主要负责加载 Android 框架的核心类库,如 java.lang 和 android.* 等包中的类。
- BootClassLoader 是由 C/C++ 代码实现的,在 Java 层无法直接获取到它的实例。
2.2 PathClassLoader
- 用于加载系统类和应用程序的类。通常,它会从应用程序的安装目录下加载 dex 文件。
- 在 Android 应用开发中,PathClassLoader 是比较常用的类加载器之一。它可以加载已经安装在设备上的应用程序的类文件。
2.3 DexClassLoader
- 允许在运行时动态加载外部的 dex 文件或包含 dex 文件的 apk 文件。
- 这使得开发者可以在应用程序运行时加载新的功能模块,实现插件化开发等高级功能。
三、类加载的过程
3.1 加载
- 通过类的全限定名,找到对应的类文件,并将其读取到内存中。这个过程可能涉及从文件系统、网络或者其他存储介质中读取类文件。
- 例如,当应用程序启动时,系统会通过类加载器加载应用程序的主类,从而启动应用程序的执行。
3.2 链接
- 验证:检查加载的类文件的正确性和安全性,确保类文件符合 Java 虚拟机规范。例如,检查类文件的格式是否正确,常量池中的符号引用是否有效等。
- 准备:为类的静态变量分配内存,并设置默认初始值。例如,对于静态 int 类型的变量,初始值为 0。
- 解析:将类文件中的符号引用转换为直接引用。例如,将类文件中对其他类的引用转换为实际的内存地址。
3.3 初始化
- 执行类的初始化代码,包括静态变量的赋值和静态代码块的执行。例如,在类的初始化代码中,可以进行一些初始化操作,如创建数据库连接、加载配置文件等。
四、Android类加载机制
4.1 双亲委派机制
4.1.1举例说明
调用 DexClassLoader 进行类加载 类A 时 , 进行如下操作 :
① DexClassLoader 查询 : 查询自己是否加载过 A ;
- 如果加载过则不需要再进行加载 ;
- 如果没有加载过 , 则向上级 PathClassLoader 询问 是否有加载过 A ;
② PathClassLoader 查询 : 查询自己是否加载过 A ;
- 如果加载过则不需要再进行加载 ;
- 如果没有加载过 , 则向上级 BootClassLoader 询问 是否有加载过 A ;
③ BootClassLoader 查询 : 查询自己是否加载过 A ;
- 如果加载过则不需要再进行加载 ;
- 如果没有加载过 , 则 查询自己是否可以加载 ;
④ BootClassLoader 查询是否可以加载 :
- 如果自己可以加载 A , 则自己加载 ;
- 如果自己不可以加载 A , 则将加载任务委派给下级 PathClassLoader ;
⑤ PathClassLoader 查询是否可以加载 :
- 如果自己可以加载 A , 则自己加载 ;
- 如果自己不可以加载 A , 则将加载任务 委派给下级 DexClassLoader ;
⑥ DexClassLoader 查询是否可以加载 :
- 如果自己可以加载 A , 则自己加载 ;
- 如果自己不可以加载 A , 则 抛出 Class Not Found 异常 ;
整个过程就是 从下到上 询问 , 然后 从上到下 委派
4.1.2 双亲委派机制的作用
4.1.2.1保证类的唯一性
- 由于父类加载器优先加载类,这样可以避免同一个类被多个不同的类加载器重复加载,从而保证了类在 JVM(在 Android 中是 ART 虚拟机或 Dalvik 虚拟机)中的唯一性。
- 例如,如果应用程序中的某个类与系统类库中的类同名,如果没有双亲委派机制,那么可能会加载两个不同的类,导致程序出现错误。
4.1.2.2提高安全性
- 双亲委派机制可以确保系统类由系统类加载器加载,而应用程序的类由应用程序类加载器加载。这样可以防止恶意代码通过自定义类加载器加载系统类,从而提高了系统的安全性。
- 例如,如果恶意代码试图加载一个伪装成系统类的恶意类,由于双亲委派机制,系统类会优先由系统类加载器加载,从而阻止了恶意代码的加载。
4.1.2.3保证类的加载顺序
- 双亲委派机制规定了类的加载顺序,即先由父类加载器加载,父类加载器无法加载时才由子类加载器加载。这样可以保证类的加载顺序符合预期,避免因类加载顺序不当而导致的错误。
- 例如,如果一个类依赖于另一个类,那么在加载这个类之前,必须先加载它所依赖的类。双亲委派机制可以确保这种依赖关系得到正确的处理。
4.2 类加载机制的作用
4.2.1 实现代码的动态加载
- 通过 DexClassLoader,可以在应用程序运行时动态加载外部的 dex 文件或 apk 文件,实现插件化开发。这使得应用程序可以在不重新安装的情况下,增加新的功能模块。
- 例如,一些游戏应用可以通过动态加载插件的方式,为玩家提供新的游戏关卡和道具。
4.2.2 提高应用程序的安全性
- 类加载机制可以对加载的类文件进行验证,确保类文件的正确性和安全性。这可以防止恶意代码的注入和攻击。
- 例如,Android 系统通过类加载机制,对应用程序的安装包进行签名验证,确保应用程序来自可信的来源。
4.2.3 实现类的隔离
- 不同的类加载器可以加载相同名称的类,但是这些类在内存中是相互隔离的。这使得不同的应用程序或者同一个应用程序的不同模块之间可以使用相同名称的类,而不会产生冲突。
- 例如,在 Android 系统中,不同的应用程序使用不同的类加载器,从而实现了应用程序之间的类隔离。
五、拓展阅读
5.1 Android 类加载机制与 Java 类加载机制有何不同
对比属性 | Java 类加载机制 | Android 类加载机制 |
---|---|---|
类加载器类型 | 启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用程序类加载器(Application ClassLoader) | BootClassLoader、PathClassLoader、DexClassLoader |
加载文件格式 | .class 文件 | 经过优化的 dex 文件 |
运行环境 | 服务器、桌面等资源相对丰富的环境 | 移动设备,资源有限 |
类加载时机 | 程序运行过程中首次使用该类时进行 | 应用程序启动时进行大量类加载,也支持运行时动态加载 |
安全机制 | 类加载器层次结构和安全管理器实现安全机制 | 除类加载器隔离机制外,还有应用程序签名、权限管理等机制 |
5.2 Android 类加载机制的优缺点分别是什么?
方面 | 优点 | 缺点 |
---|---|---|
灵活性 | 可通过 DexClassLoader 实现插件化开发等高级功能,提高开发灵活性。 | 复杂性较高,涉及多种类加载器和复杂加载过程,理解和掌握较难,易出现类加载相关问题。 |
资源管理 | 加载类时进行优化,文件更紧凑,占用内存小,能合理管理内存和资源。 | 动态加载类时存在一定性能开销,频繁动态加载可能影响应用性能和响应速度。 |
安全性 | 通过应用程序签名、权限管理等机制确保安全性,类加载器层次结构提供安全隔离。 | / |
适应环境 | 针对移动设备资源有限性进行优化,能快速加载类,减少启动时间,提高用户体验。 | 随着系统升级可能出现版本兼容性问题,开发者需关注系统更新并调整类加载策略。 |
5.3 Android 类加载机制的发展历程
时代 | 类加载器类型 | 类加载过程 | 特点 |
---|---|---|---|
Dalvik 时代 | BootClassLoader、PathClassLoader、DexClassLoader | 加载(找到 dex 文件并读入内存)、验证(检查格式和内容)、转换(将 Dalvik 字节码转换为机器码)、初始化(执行初始化代码) | Dalvik 字节码经过优化占用内存小,适合移动设备,但性能相对低、启动时间长 |
ART 时代 | BootClassLoader、PathClassLoader、DexClassLoader | 加载(同 Dalvik)、验证(更严格)、编译(安装时或首次运行时将 dex 文件编译为本地机器码)、初始化(执行初始化代码) | 采用 AOT 和 JIT 编译结合提高性能和启动速度,支持多 dex 文件优化、运行时内存管理等高级特性 |
现代 Android | 优化后的 PathClassLoader、DexClassLoader 等,引入 InMemoryDexClassLoader 等 | 类似 ART,不断优化效率和性能 | 类加载器优化提高效率和性能,支持插件化开发,增强安全性 |
附录
【Java 虚拟机原理】Android 类加载机制 ( 双亲委派机制 | BootClassLoader | PathClassLoader | DexClassLoader )