ClassLoader类加载源码解析

 更新时间:2019年09月16日 15:32:44   作者:droidDing  
这篇文章主要为大家详细解析了ClassLoader类加载源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Java类加载器

1、BootClassLoader: 用于加载Android Framework层class文件。
2、PathClassLoader: 用于Android应用程序类加载器。可以加载指定的dex,jar、zip、zpk中的classes.dex
3、DexClassLoader:加载指定的dex,以及jar、zip、apk中的classes.dex

源码解析

1.ClassLoader中提供loadClass用于加载指定类

//ClassLoader.java
public Class<?> loadClass(String name) throws ClassNotFoundException {
 //该处调用了两个参数的重载方法
  return loadClass(name, false);
 }
 
 protected Class<?> loadClass(String name, boolean resolve)
  throws ClassNotFoundException
 {
  //先查一下该类是否已经加载过了
   Class<?> c = findLoadedClass(name);
   if (c == null) {
    try {
     //双亲委托机制,先让爸爸去找
     if (parent != null) {
      c = parent.loadClass(name, false);
     } else {
      //如果parent为null,则用BootClassLoader进行加载
      c = findBootstrapClassOrNull(name);
     }
    } catch (ClassNotFoundException e) {
     // ClassNotFoundException thrown if class not found
     // from the non-null parent class loader
    }

    if (c == null) {
     //如果都找不到就自己去找,此方法在子类BaseDexClassLoader类中有重写
     c = findClass(name);
    }
   }
   return c;
 }

2.BaseDexClassLoader类中对findClass有重写,也是实际会使用执行的

//BaseDexClassLoader.java
//查找class
 @Override
 protected Class<?> findClass(String name) throws ClassNotFoundException {
  ...
  //这里通过pathList变量来查找,而pathList是在BaseDexClassLoader的构造方法中初始化的
  Class c = pathList.findClass(name, suppressedExceptions);
  ...
  return c;
 }
 
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
   String librarySearchPath, ClassLoader parent, boolean isTrusted) {
  super(parent);
  //构造方法中初始化pathList变量
  this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
 }

3.BaseDexClassLoader中是通过调用DexPathList中的findClass来实现的,那么接下来我们分析一下DexPathList是怎么实现的

//DexPathList.java
//是一个Element数组,一个element中包含一个 DexFile,DexFile就代表一个Dex文件,里面的native(C/C++)函数来进行Dex的加载工作
 private Element[] dexElements;
 
public Class<?> findClass(String name, List<Throwable> suppressed) {
  for (Element element : dexElements) {
   //此处调用Element的findClass来实现,
   Class<?> clazz = element.findClass(name, definingContext, suppressed);
   if (clazz != null) {
    return clazz;
   }
  }
  return null;
 }
// Element为DexPathList的内部类
static class Element {
 private final File path;
  //一个DexFile就代表一个Dex文件
  private final DexFile dexFile;
  //有多个构造方法,但都仅是将值传过来,让Element来持有一个DexFile
  public Element(DexFile dexFile) {
 this.dexFile = dexFile;
   this.path = null;
 }
  
  public Class<?> findClass(String name, ClassLoader definingContext,
    List<Throwable> suppressed) {
    //通过DexFile来加载类
   return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
     : null;
  }
 }

DexPathList(ClassLoader definingContext, String dexPath,
   String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
   //通过makeDexElements方法为dexElements初始化
 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
           suppressedExceptions, definingContext, isTrusted);
}
//腾讯系的热修复,诸如微信tinker、qq空间qfix原理便是反射此方法,将修复后的类打包成dex,通过反射该方法来将文件转化为Element,并将新生成的element放到dexElements前面,这样下次系统再去寻找某个class时,会先从修复后的dex中来找class,找到后便不再继续查找,从而修复该class,此方式便为插桩
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
   List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
  Element[] elements = new Element[files.size()];
  ...
  for (File file : files) {
 if (name.endsWith(DEX_SUFFIX)) {
    //以 .dex 结尾的
     // Raw dex file (not inside a zip/jar).
     //加载dex文件
      dex = loadDexFile(file, optimizedDirectory, loader, elements);
      if (dex != null) {
       elements[elementsPos++] = new Element(dex, null);
      }
    }
 }
   ...
  return elements;
 }

private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
          Element[] elements)
   throws IOException {
  if (optimizedDirectory == null) {
   return new DexFile(file, loader, elements);
  } else {
   String optimizedPath = optimizedPathFor(file, optimizedDirectory);
   return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
  }
 }

4.这里通过 new DexFile 或者 loadDex方法来创建DexFile,两者类似,那我们拿new DexFile 来举例分析

//DexFile.java
private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
   DexPathList.Element[] elements) throws IOException {
  ...
  //此处调用openDexFile来实现
  mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
  ...
 }

private static Object openDexFile(String sourceName, String outputName, int flags,
   ClassLoader loader, DexPathList.Element[] elements) throws IOException {
  //此处通过调用 openDexFileNative来实现
  return openDexFileNative(new File(sourceName).getAbsolutePath(),
         (outputName == null)
          ? null
          : new File(outputName).getAbsolutePath(),
         flags,
         loader,
         elements);
 }
//openDexFileNative是一个native方法,是由C/C++来实现的
private static native Object openDexFileNative(String sourceName, String outputName, int flags,
   ClassLoader loader, DexPathList.Element[] elements);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java实现文件检索系统的示例代码

    Java实现文件检索系统的示例代码

    这篇文章主要为大家详细介绍了如何刘Java语言实现简易的文件检索系统,文中的示例代码讲解详细,对我们学习Java开发有一定的帮助,需要的可以参考一下
    2022-07-07
  • 详解Java异常处理最佳实践及陷阱防范

    详解Java异常处理最佳实践及陷阱防范

    这篇文章主要介绍了Java异常处理最佳实践及陷阱防范,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Apache Commons Math3探索之多项式曲线拟合实现代码

    Apache Commons Math3探索之多项式曲线拟合实现代码

    这篇文章主要介绍了Apache Commons Math3探索之多项式曲线拟合实现代码,小编觉得挺不错的,这里分享给大家,供需要的朋友参考。
    2017-10-10
  • Springboot设置默认访问路径方法实现

    Springboot设置默认访问路径方法实现

    这篇文章主要介绍了Springboot设置默认访问路径方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 关于BufferedReader读取文件指定字符集问题

    关于BufferedReader读取文件指定字符集问题

    这篇文章主要介绍了关于BufferedReader读取文件指定字符集问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 解决SpringBoot application.yaml文件配置schema 无法执行sql问题

    解决SpringBoot application.yaml文件配置schema 无法执行sql问题

    这篇文章主要介绍了解决SpringBoot application.yaml文件配置schema 无法执行sql问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 全面分析Java方法的使用与递归

    全面分析Java方法的使用与递归

    在java中,方法就是用来完成解决某件事情或实现某个功能的办法;程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。但是如果没终止条件会造成死循环,所以递归代码里要有结束自调自的条件,本篇接下来讲解一下方法与递归
    2022-04-04
  • 基于String变量的两种创建方式(详解)

    基于String变量的两种创建方式(详解)

    下面小编就为大家带来一篇基于String变量的两种创建方式(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • 解决Java项目启动报错:Logback configuration error detected:问题

    解决Java项目启动报错:Logback configuration error detected:问题

    这篇文章主要介绍了解决Java项目启动报错:Logback configuration error detected:问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • java中实现excel合并单元格详细代码实例

    java中实现excel合并单元格详细代码实例

    最近的工作中,遇到一个需求在生成的Excel表格后需要在尾部添加一个合并的单元格数据,这篇文章主要给大家介绍了关于java中实现excel合并单元格的相关资料,需要的朋友可以参考下
    2024-06-06

最新评论