EP4-为什么PluginClassLoader对loadcass()进行覆写

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

你可能感兴趣的:(EP4-为什么PluginClassLoader对loadcass()进行覆写)