欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

ClassLoader类加载源码解析,加载源码

来源: javaer 分享于  点击 45394 次 点评:113

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);

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

相关栏目:

用户点评