前言
无论是 Java 还是 Android,学习它们的类加载机制都非常重要的。本文统一记录两个平台下 ClassLoader 的实现。
一、Java 中的 ClassLoader
1.1 Bootstrap ClassLoader 引导类加载器
- 作用:
加载 Java 系统类,如java.lang.*、java.uti.*
等。JVM 的启动也是由它创建的初始类来完成的。 - 特点:
C/C++ 实现;
不继承于java.lang.ClassLoader
。
1.2 Extensions ClassLoader 扩展类加载器
- 作用:
加载 Java 扩展类,比如 swing 系列(图形化)、内置的 js 引擎、xml 解析器等等。
1.3 AppClassLoader 应用程序加载器
- 作用:
加载当前应用程序 Classpath 目录下的所有 jar 和 Class 文件。可指定目录。
1.4 CustomClassLoader 自定义加载器
- 作用:
自己实现的加载器。 - 特点:
继承于 java.lang.ClassLoader。
Extensions ClassLoader 和 App ClassLoader 也继承了java.lang.ClassLoader 类。
Java 类加载器继承关系
- ClassLoader:是抽象类,定义类加载器主要功能;
- SecureClassLoader:继承了 ClassLoader,加入权限相关功能,加强安全性;
- URLClassLoader:通过 Uri 路径从 jar 文件和文件夹中加载类和资源;
- ExtClassLoader 和 AppClassLoader 都继承自 URLClassLoader,都是 Launcher 的内部类。
Launcher 是 Java 虚拟机的入口应用,ExtClassLoader 和 AppClassLoader 都是在 Launcher 中进行初始化的。
双亲委托(Parents Delegation Model)
- 定义:
所谓的双亲委托,也就是这些 ClassLoader 在加载类或接口时,首先判断该 Class 是否已经加载。如果没有加载则去委托给父类去加载,这样依次查找。
到最后会找到最顶层的 Bootstrap ClassLoader,如果找到或成功加载该 Class 则返回。这样就完成了一个所谓的 "Parents Delegation" 的过程。
如果最顶层没有返回,则只能自己加载了。
package java.lang ClassLoader # loadClass 方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
作用:
至于为什么采用这种模式,是有原因的。Java 中判断两个类是否相等,也包括匹配两者的类加载器。如果两个类的加载器不同,就算他们名字一样、路径一样,依旧会被认为不是同一个类。
这里的相等包含:equals() 方法、isAssignableFrom() 方法、isInstantce() 方法。优点:
避免重复加载;
更加安全,不容易被随意篡改。
1.5 自定义 ClassLoader
- 重写
findClass()
方法,调用loadClass()
时如果父加载器不为空或不能加载,会调用该方法进行加载。 - 加载 Class 文件,这里是使用文件流加载。
public class MyClassLoader extends ClassLoader {
// 1. 重写 findClass 方法
@Override
protected Class<?> findClass(String name) {
Class clazz = null;
byte[] classData = loadData();
if (null != classData) {
clazz = defineClass(name, classData, 0, classData.length);
}
return clazz;
}
// 2.从磁盘加载文件
private byte[] loadData() {
File file = new File("D:\\Test.class");
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != out) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
- 进行调试,最后打印出来的类名为 TestClass。
public static void main(String[] args) {
MyClassLoader myClassLoader = new MyClassLoader();
try {
// 指定类名加载,也可以扩展一下遍历加载文件夹下所有文件
Class c = myClassLoader.loadClass("com.sky.test.Test");
System.out.print(c.getSimpleName());//TestClass
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
二、Android 中的 ClassLoader
Android 中的 ClassLoader 以及继承结构:
ClassLoader:顶级父类。
-
BootClassLoader:Android 系统启动时预加载常用类。
-
BaseDexClassLoader
-
PathClassLoader:加载指定目录下的类;
-
DexClassLoader:加载指定目录下的类;
-
InMemoryDexClassLoader:加载内存中的类;
-
-
SecureClassLoader:扩展权限功能,增加安全性。
- URLClassLoader:加载 URL 路径的类和资源。
2.1 BootClassLoader
java.lang.ClassLoader 内部类 BootClassLoader
class BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
@FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
}
return instance;
}
public BootClassLoader() {
super(null);
}
}
- 作用:
Android 系统启动时,预加载常用类。 - 特点:
java 实现;
ClassLoader 内部类,同包访问;
功能大多使用 VMClassLoader(虚拟机 ClassLoader) 调用 native 方法实现。很好理解,因为 VM 是 C/C++ 实现的,所以会调用 native 方法去加载类或资源。
2.2 PathClassLoader
dalvik.system.PathClassLoader
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
- 作用:
加载 dex 文件,各种形式的(dex/apk/jar) 。 - 构造器:
dexPath:文件路径;
librarySearchPath:包含 C、C++库的路径集合;
parent:父加载器。
2.3 DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
- 作用:
加载 dex 文件,各种形式的(dex/apk/jar )。 - 构造器
dexPath :dex 文件路径集合,多个文件用分隔符分割,默认分隔符 “:”;
optimizedDirectory:已解压 dex 文件路径,该路径必须为内部存储路径。一般为:/data/data/<Package Name>/...
,作为缓存路径使用。
librarySearchPath:库的路径集合,可以为 null;
parent:父加载器。
PathClassLoader 和 DexClassLoader 异同
DexClassLoader 和 PathDexClassLoader 都能加载各处的 dex 文件。
API26 之前版本传递 optimizedDirectory 参数有不同区别:
- DexClassLoader 传参 optimizedDirectory 可以自定义 dex 优化后存放的路径。
- PathDexClassLoader 传 null,缓存到默认 data/dalvik-cache/ 路径中。
API26 之后统一了缓存 dex 文件的路径,optimizedDirectory 参数已弃用。统一存放到了 dex 文件同级目录下的 oat/< isa > 文件作为缓存文件的存储目录。
2.4 BootClassLoader 创建过程
Zygote 创建时,调用 ZygoteInit 方法,这个过程中创建了 BootClassLoader :
- ZygoteInit 调用 preload() 方法进行预加载;
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
...
// 预加载方法
preload(bootTimingsTraceLog);
...
}
- preload() 方法调用 preloadClasses(); 用来加载 Class
static void preload(TimingsTraceLog bootTimingsTraceLog) {
...
preloadClasses();
...
}
- preloadClasses() 方法以流的方式,从系统指定目录预加载 Class。
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
// 从目录创建文件输入流
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
try {
// 使用 BufferedReader 读取流,带缓存 读取快
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
// 读取到的数据转换为 Class
Class.forName(line, true, null);
count++;
} catch (ClassNotFoundException e) {
Log.w(TAG, "Class not found for preloading: " + line);
}
...
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
...
}
}
- PRELOADED_CLASSES 路径下的文件,描述了要预加载的类。这里 Zygote 加载了一些通用类,应用程序进程在创建时就无需再次加载了。
frameworks/base/config/preloaded-classes
...
android.app.Dialog
android.app.Dialog$ListenersHandler
android.app.DialogFragment
...
android.app.Fragment
android.app.Fragment$1
android.app.Fragment$AnimationInfo
android.app.Fragment$OnStartEnterTransitionListener
...
android.app.Activity
android.app.Activity$HostCallbacks
android.app.ActivityManager
android.app.ActivityManager$1
android.app.ActivityManager$AppTask
android.app.ActivityManager$MemoryInfo
- forName 方法根据类名加载 Class。并且如果 loader 为null,就创建了 BootClassLoader。
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
// 创建 BootClassLoader
loader = BootClassLoader.getInstance();
}
Class<?> result;
try {
// 调用 native 方法加载 Class
result = classForName(name, initialize, loader);
} catch (ClassNotFoundException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
throw e;
}
return result;
}
调用 native 方法加载 Class。
@FastNative
static native Class<?> classForName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException;
2.5 PathClassLoader 创建过程
- ZygoteInit 中 main 方法会根据参数进行系统初始化
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
... // preLoad 在之前,也就是 BootClassLoader 先创建预加载
boolean startSystemServer = false;
...
for (int i = 1; i < argv.length; i++) {
...
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
}
...
}
if (startSystemServer) {
// 看这里
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
}
- forkSystemServer() 方法 fork 系统进程:
private static Runnable forkSystemServer(String abiList, String socketName,ZygoteServer zygoteServer) {
...
int pid;
/* Request to fork the system server process */
// 2.1 创建系统进程,返回 pid
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
...
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
// 2.2 处理系统进程
return handleSystemServerProcess(parsedArgs);
}
return null;
}
- 2.1 forkSystemServer() 调用 native 方法 fork 系统进程。
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
...
int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities);
...
return pid;
}
native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
- 2.2 pid == 0,调用 handleSystemServerProcess() 方法处理系统进程逻辑。
private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
...
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
- createPathClassLoader() 使用 ClassLoaderFactory 创建 PathClassLoader。
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
null /* classLoaderName */);
frameworks/base/core/java/com/android/internal/os/ClassLoaderFactory.java
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, ClassLoader parent, String classloaderName) {
// 进行判断,如果 classloaderName 为 null 或 PathClassLoader.class.getName() 则创建 PathClassLoader
if (isPathClassLoaderName(classloaderName)) {
return new PathClassLoader(dexPath, librarySearchPath, parent);
} else if (isDelegateLastClassLoaderName(classloaderName)) {
return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
}
throw new AssertionError("Invalid classLoaderName: " + classloaderName);
到这里就完成了 PathClassLoader 的创建过程。
总结
Java 中的 ClassLoader:
名称 | 作用 |
---|---|
ClassLoader | ClassLoader 的抽象 |
SecureClassLoader | 加入权限相关功能,加强安全性 |
URLClassLoader | 通过 Uri 读取文件 |
Bootstrap ClassLoader | 加载 Java 系统类 |
Extensions ClassLoader | 加载 Java 扩展类 |
AppClassLoader | 加载当前应用程序文件 |
ExtClassLoader | 加载 ext 目录下文件 |
自定义 ClassLoader | 加载用户指定 Class 文件 |
Android 中的 ClassLoader:
名称 | 作用 |
---|---|
ClassLoader | ClassLoader 的抽象 |
BootClassLoader | Android 系统启动时预加载常用类 |
PathClassLoader | 加载指定目录下的类 |
DexClassLoader | 加载指定目录下的类 |
InMemoryDexClassLoader | 加载内存中的类 |
SecureClassLoader | 扩展权限功能,增加安全性 |
URLClassLoader | 加载 URL 路径的类和资源 |