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.
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.