ClassLoader:Java Android 总结

前言

无论是 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

  1. 重写 findClass() 方法,调用 loadClass() 时如果父加载器不为空或不能加载,会调用该方法进行加载。
  2. 加载 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;
    }
}
  1. 进行调试,最后打印出来的类名为 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:顶级父类。

  • ↪ \hookrightarrow BootClassLoader:Android 系统启动时预加载常用类。
  • ↪ \hookrightarrow BaseDexClassLoader
    • ↪ \hookrightarrow PathClassLoader:加载指定目录下的类;
    • ↪ \hookrightarrow DexClassLoader:加载指定目录下的类;
    • ↪ \hookrightarrow InMemoryDexClassLoader:加载内存中的类;
  • ↪ \hookrightarrow 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 :

  1. ZygoteInit 调用 preload() 方法进行预加载;

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]) {
	...
	// 预加载方法
	preload(bootTimingsTraceLog);
	...
}
  1. preload() 方法调用 preloadClasses(); 用来加载 Class
static void preload(TimingsTraceLog bootTimingsTraceLog) {
	...
	preloadClasses();
	...
}
  1. 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
  1. 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 创建过程

  1. 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;
		}
	}
}
  1. 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);
}
  1. 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:

名称作用
ClassLoaderClassLoader 的抽象
SecureClassLoader加入权限相关功能,加强安全性
URLClassLoader通过 Uri 读取文件
Bootstrap ClassLoader加载 Java 系统类
Extensions ClassLoader加载 Java 扩展类
AppClassLoader加载当前应用程序文件
ExtClassLoader加载 ext 目录下文件
自定义 ClassLoader加载用户指定 Class 文件

Android 中的 ClassLoader:

名称作用
ClassLoaderClassLoader 的抽象
BootClassLoaderAndroid 系统启动时预加载常用类
PathClassLoader加载指定目录下的类
DexClassLoader加载指定目录下的类
InMemoryDexClassLoader加载内存中的类
SecureClassLoader扩展权限功能,增加安全性
URLClassLoader加载 URL 路径的类和资源
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值