0x00 为什么PluginClassLoader对loadcass()进行覆写
昨天说到PluginClassLoader对ClassLoader.java里的loadclass()方法进行了覆写,具体就是把loadClass()函数体中的:
Class<?> clazz = findLoadedClass(className);
替换成了:
if (child != null && nestedLoadLevels > 0) {
nestedLoadLevels = 0;
// 再次尝试父类装载器,模拟抛出异常等操作
return getParent().loadClass(className);
}
其中findLoadedClass的注释是:
Returns the class with the specified name if it has already been loaded by the VM or {@code null} if it has not yet been loaded.
如果一个类已经被VM装载过了,就返回这个Class。
覆写之后要判断child classloader是否为空。nestedLoadLevels是loadClass的嵌套调用次数,不为0时说明是内部调用,直接返回。这句话是什么意思?我们看PluginClassLoader.java,覆写了findClass();
protected Class<?> findClass(String className)
throws ClassNotFoundException {
if (child == null) {
nestedLoadLevels = 0;
return super.findClass(className);
}
try {
return super.findClass(className);
} catch (ClassNotFoundException e) {
// 尝试使用child装载
nestedLoadLevels++;
return child.loadClass(className);
} finally {
nestedLoadLevels = 0;
}
}
我唯一看到nestedLoadLevels自加的地方就是在super.findClass也就是ClassLoader中的findClass找不到对应类名的时候。比如loadclass的时候找不到这个类了,我们知道找类会先去找load parent有没有加载过这个类,这里findClass重写的逻辑是:
1.判断子类有没有被加载过,如果没有,调用父类的findclass方法找这个类。
2.如果子类已经被加载过,尝试查找这个类。如果没找到(why?),就尝试使用child装载。
之前我们分析过,findClass会调用BaseDexClassLoader中的:
Class clazz = pathList.findClass(name);
最终调用的是DexPathList中的findClass():
/**
* Finds the named class in one of the dex files pointed at by
* this instance. This will find the one in the earliest listed
* path element. If the class is found but has not yet been
* defined, then this method will define it in the defining
* context that this instance was constructed with.
*
* @return the named class or {@code null} if the class is not
* found in any of the dex files
*/
//找到最早在path元素中罗列出的类(我理解是根节点的那个类)
public Class findClass(String name) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext);
if (clazz != null) {
return clazz;
}
}
}
return null;
}
PluginClassLoader中的findclass改变了类装载的顺序,先去子类中findClass,找不到才去走正常的loadClass流程。
-NOV 24