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:

名称 作用
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 路径的类和资源
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容