Search code examples
androidclassloaderpatch

Why MyFragment is loaded by PathClassLoader not MyClassLoader


we are using custom MyClassLoader extends ClassLoader to replace default PathClassLoader, like:

    Context mBase = (Context) getFieldValue(application, "mBase");
    Object mPackageInfo = getFieldValue(mBase, "mPackageInfo");
    ORIGINAL_CLASS_LOADER = (ClassLoader) getFieldValue(mPackageInfo, "mClassLoader");
    CUSTOMED_CLASS_LOADER = new MyClassLoader(ORIGINAL_CLASS_LOADER);

executed when application is onCreat.

The first problem is, Not all classes is loaded first by MyClassLoader, just MyActivitys and MyViews are loaded by MyClassLoader, MyFragments are still loaded by PathClassLoader. Does this have something todo with Context?

In MyClassLoader we load class by

    public Class<?> loadClass(String className)
            throws ClassNotFoundException {
        try {
            ClassLoader myDexClassLoader = loaders.get("DEX");
            if(myDexClassLoader != null){
                Class<?> c = null;
                try {
                     c = myDexClassLoader.loadClass(className);
                    // c = loader.findClass(className);
                } catch (ClassNotFoundException e) {
                }
                if (c != null) {
                    return c;
                }
            }
        } 
        return super.loadClass(className);
    }`

and myDexClassLoader is defined as

    public static class MyDexClassLoader extends DexClassLoader {

    public MyDexClassLoader(String dexPath, String dexOutputDir,
            String libPath, ClassLoader parent) {
        super(dexPath, dexOutputDir, libPath, parent);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return super.findClass(name);
    }

    @SuppressLint("NewApi")
    @Override
    public Class<?> loadClass(String className)
            throws ClassNotFoundException {
        Class<?> clazz = null;
        try {
            clazz = findClass(className);
        } catch (ClassNotFoundException e) {
        }
        if (clazz != null) {
            return clazz;
        }
        return super.loadClass(className);
    }

}

MyDexClassLoader is inited with PatchDex file contains classes like A1 and C1.

PathClassLoader is both MyDexClassLoader and MyClassLoader 's parent.

And in OriginalApk we have the original class A1 B1 C1.

The second problems is, we can print in the loadClass method, that new A1 is loaded from PatchDex by MyDexClassLoader (called by MyClassLoader),

but as class B1 is not in the PatchDex, MyDexClassLoader ( why not first called by MyClassLoader ) finally use it's parent(PathClassLoader) to load the old B1,

class B1 is using C1 directly, so we have to load class C1. By print loadClass method we found MyDexClassLoader has taken control, but not using it's PatchDex, just using parent's PathClassLoader's old C1.

How does this happen? Why MyDexClassLoader load class C1 directly by it's parent, but not it's PatchDex when it has ?

Thanks in advance.


Solution

  • the classloaders in andorid for dex is different from java's classloader.

    for example, pathclassloader and dexclassloader doesn't reference child anymore. these classloader has its own dex file lists for load class from dex files that registered.

    so, do not use super class first. must find class from your own custom class loader first.