详解Android类加载ClassLoader

详解Android类加载ClassLoader,第1张

概述基本知识Java的类加载设计了一套双亲代理的模式,使得用户没法替换系统的核心类,从而让应用更安全。所谓双亲代理就是指,当加载类的时候首先去Bootstrap中加载类,如果没有则去Extension中加载,如果再没有才去App

基本知识@H_403_3@

Java的类加载设计了一套双亲代理的模式,使得用户没法替换系统的核心类,从而让应用更安全。所谓双亲代理就是指,当加载类的时候首先去bootstrap中加载类,如果没有则去Extension中加载,如果再没有才去AppClassLoader中去加载。从而实现安全和稳定。@H_403_3@

Java ClassLoader@H_403_3@

bootstrapClassLoader@H_403_3@

引导类加载器 ,用来加载Java的核心库。通过底层代码来实现的,基本上只要parent为null,那就表示引导类加载器。@H_403_3@

比如:charsets.jar、deploy.jar、javaws.jar、jce.jar、jfr.jar、jfxswt.jar、Jsse.jar、management-agent.jar、plugin.jar、resources.jar、rt.jar@H_403_3@

ExtClassLoader@H_403_3@

拓展类加载器 ,用来加载Java的拓展的类库, ${JAVA_HOME}/jre/lib/ext/ 目录中的所有jar。@H_403_3@

比如:cldrdata.jar、dnsns.jar、jfxrt.jar、localedata.jar、nashorn.jar、sunec.jar、sunjce_provIDer.jar、sunpkcs11.jar、zipfs.jar等等@H_403_3@

AppClassLoader@H_403_3@

系统类加载器 (不要被名字给迷惑),用来加载Java应用中的类。一般来说自己写的类都是通过这个加载的。而Java中 ClassLoader.getSystemClassLoader() 返回的就是AppClassLoader。(AndroID中修改了ClassLoader的逻辑,返回的会是一个PathClassLoader)@H_403_3@

自定义ClassLoader@H_403_3@

用户如果想自定义ClassLoader的话,只需要继承自 java.lang.classLoader 即可。@H_403_3@

ClassLoader中与加载类相关的方法:@H_403_3@ getParent() 返回该类加载器的父类加载器。 loadClass(String name) 加载名称为 name的类,返回的结果是 java.lang.class类的实例。 findClass(String name) 查找名称为 name的类,返回的结果是 java.lang.class类的实例。 findLoadedClass(String name) 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.class类的实例。 defineClass(String name,byte[] b,int off,int len) 把字节数组 b中的内容转换成 Java 类,返回的结果是 java.lang.class类的实例。这个方法被声明为 final的。

也许你不太了解上面几个函数的区别,没关系,我们来看下源码是如何实现的。@H_403_3@

//ClassLoader.javaprotected Class<?> loadClass(String name,boolean resolve)  throws ClassNotFoundException{    // First,check if the class has already been loaded    Class c = findLoadedClass(name);    if (c == null) {      long t0 = System.nanoTime();      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.        long t1 = System.nanoTime();        c = findClass(name);        // this is the defining class loader; record the stats      }    }    return c;}

所以优先级大概如下:@H_403_3@

loadClass → findLoadedClass → parent.loadClass/findbootstrapClassOrNull → findClass → defineClass@H_403_3@

AndroID ClassLoader@H_403_3@

在AndroID中ClassLoader主要有两个直接子类,叫做 BaseDexClassLoader 和 SecureClassLoader 。而前者有两个直接子类是 PathClassLoader 和 DexClassLoader (AndroID O添加了 InMemoryDexClassLoader ,略)。@H_403_3@

我们只讨论PathClassLoader和DexClassLoader@H_403_3@

PathClassLoader@H_403_3@

用来加载安装了的应用中的dex文件。它也是AndroID里面的一个最核心的ClassLoader了。相当于Java中的那个AppClassLoader。@H_403_3@

public class PathClassLoader extends BaseDexClassLoader {  /**   * Creates a {@code PathClassLoader} that operates on a given List of files   * and directorIEs. This method is equivalent to calling   * {@link #PathClassLoader(String,String,ClassLoader)} with a   * {@code null} value for the second argument (see description there).   *   * @param dexPath the List of jar/apk files containing classes and   * resources,delimited by {@code file.pathSeparator},which   * defaults to {@code ":"} on AndroID   * @param parent the parent class loader   */  public PathClassLoader(String dexPath,ClassLoader parent) {    super(dexPath,null,parent);  }  /**   * Creates a {@code PathClassLoader} that operates on two given   * Lists of files and directorIEs. The entrIEs of the first List   * should be one of the following:   *   * <ul>   * <li>JAR/ZIP/APK files,possibly containing a "classes.dex" file as   * well as arbitrary resources.   * <li>Raw ".dex" files (not insIDe a zip file).   * </ul>   *   * The entrIEs of the second List should be directorIEs containing   * native library files.   *   * @param dexPath the List of jar/apk files containing classes and   * resources,which   * defaults to {@code ":"} on AndroID   * @param librarySearchPath the List of directorIEs containing native   * librarIEs,delimited by {@code file.pathSeparator}; may be   * {@code null}   * @param parent the parent class loader   */  public PathClassLoader(String dexPath,String librarySearchPath,librarySearchPath,parent);  }}

它的实例化是通过调用 ApplicationLoaders.getClassLoader 来实现的。@H_403_3@

它是在ActivityThread启动时发送一个BIND_APPliCATION消息后在handleBindApplication中创建ContextImpl时调用LoadedApk里面的 getResources(ActivityThread mainThread) 最后回到ActivityThread中又调用LoadedApk的 getClassLoader 生成的,具体的在LoadedApk的 createOrUpdateClassLoaderLocked 。@H_403_3@

那么问题来了,当AndroID加载class的时候,LoadedApk中的ClassLoader是怎么被调用到的呢?@H_403_3@

其实Class里面,如果你不给ClassLoader的话,它默认会去拿Java虚拟机栈里面的 CallingClassLoader ,而这个就是LoadedApk里面的同一个ClassLoader。@H_403_3@

//Class.javapublic static Class<?> forname(String classname)      throws ClassNotFoundException {  return forname(classname,true,VMStack.getCallingClassLoader());}

查看VMStack的源码发现 getCallingClassLoader 其实是一个native函数,AndroID通过底层实现了这个。@H_403_3@

//dalvik.system.VMStack/** * Returns the defining class loader of the caller's caller. * * @return the requested class loader,or {@code null} if this is the *     bootstrap class loader. */@FastNativenative public static ClassLoader getCallingClassLoader();

底层想必最终也是拿到LoadedApk里面的ClassLoader。@H_403_3@

DexClassLoader@H_403_3@

它是一个可以用来加载包含dex文件的jar或者apk文件的,但是它可以用来加载非安装的apk。比如加载sdcard上面的,或者NetWork的。@H_403_3@

public class DexClassLoader extends BaseDexClassLoader {  /**   * Creates a {@code DexClassLoader} that finds interpreted and native   * code. Interpreted classes are found in a set of DEX files contained   * in Jar or APK files.   *   * <p>The path Lists are separated using the character specifIEd by the   * {@code path.separator} system property,which defaults to {@code :}.   *   * @param dexPath the List of jar/apk files containing classes and   *   resources,which   *   defaults to {@code ":"} on AndroID   * @param optimizedDirectory directory where optimized dex files   *   should be written; must not be {@code null}   * @param librarySearchPath the List of directorIEs containing native   *   librarIEs,delimited by {@code file.pathSeparator}; may be   *   {@code null}   * @param parent the parent class loader   */  public DexClassLoader(String dexPath,String optimizedDirectory,new file(optimizedDirectory),parent);  }}

比如现在很流行的插件化/热补丁,其实都是通过DexClassLoader来实现的。具体思路是: 创建一个DexClassLoader,通过反射将前者的DexPathList跟系统的PathClassLoader中的DexPathList合并,就可以实现优先加载我们自己的新类,从而替换旧类中的逻辑了。
@H_403_3@

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。@H_403_3@ 总结

以上是内存溢出为你收集整理的详解Android类加载ClassLoader全部内容,希望文章能够帮你解决详解Android类加载ClassLoader所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1146311.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存