dexpathlist.java_ClassLoader类加载源码解析

Java类加载器

1、BootClassLoader: 用于加载Android Framework层class文件。

2、PathClassLoader: 用于Android应用程序类加载器。可以加载指定的dex,jar、zip、zpk中的classes.dex

3、DexClassLoader:加载指定的dex,以及jar、zip、apk中的classes.dex

04b4eddbc992f2925716ad73ce275781.png

712b84456c676ac23f268049aafafa3b.png

源码解析

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 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 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 files, File optimizedDirectory,

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值