0x00
在DexClassLoader和PathClassLoader加载Dex流程一文中,我们分析了dex文件如何形成了DexFile结构体。本文中讲解类加载机制,实际上就是生成ClassObject对象。
我们以DexClassLoader为例,讲解类加载机制,PathClassLoader是一样的。
我们在加载类时通常会调用loadClass,那么我们就从loadClass来开始分析。
0x01
DexClassLoader类没有loadClass方法,所以调用的是父类ClassLoader类的loadClass方法,ClassLoader类的loadClass方法位于libcore\luni\src\main\java\java\lang\ClassLoader.java中。
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
}
if (clazz == null) {
clazz = findClass(className);
}
}
return clazz;
}
DexClassLoader复写了父类ClassLoader的findClass方法,所以调用子类DexClassLoader类的方法findClass,代码位于libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (VERBOSE_DEBUG)
System.out.println("DexClassLoader " + this
+ ": findClass '" + name + "'");
int length = mFiles.length;
for (int i = 0; i < length; i++) {
if (VERBOSE_DEBUG)
System.out.println(" Now searching: " + mFiles[i].getPath());
if (mDexs[i] != null) {
String slashName = name.replace('.', '/');
Class clazz = mDexs[i].loadClass(slashName, this);
if (clazz != null) {
if (VERBOSE_DEBUG)
System.out.println(" found");
return clazz;
}
}
}
throw new ClassNotFoundException(name + " in loader " + this);
}
这里调用的是DexFile类的loadClass方法,代码位于libcore\dalvik\src\main\java\dalvik\system\DexFile.java。
public Class loadClass(String name, ClassLoader loader) {
String slashName = name.replace('.', '/');
return loadClassBinaryName(slashName, loader);
}
public Class loadClassBinaryName(String name, ClassLoader loader) {
return defineClass(name, loader, mCookie,
null);
//new ProtectionDomain(name) /*DEBUG ONLY*/);
}
defineClass对应的是JNI方法,如下:
native private static Class defineClass(String name, ClassLoader loader,
int cookie, ProtectionDomain pd);
还记得
在
DexClassLoader和PathClassLoade
r加载Dex流程一文中,openDexFile也是JNI方法,
对应的native方法位于dalvik\vm\native\dalvik_system_DexFile.c。
const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
{ "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I",
Dalvik_dalvik_system_DexFile_openDexFile },
{ "closeDexFile", "(I)V",
Dalvik_dalvik_system_DexFile_closeDexFile },
{ "defineClass", "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",
Dalvik_dalvik_system_DexFile_defineClass },
{ "getClassNameList", "(I)[Ljava/lang/String;",
Dalvik_dalvik_system_DexFile_getClassNameList },
{ "isDexOptNeeded", "(Ljava/lang/String;)Z",
Dalvik_dalvik_system_DexFile_isDexOptNeeded },
{ NULL, NULL, NULL },
};
defineClass对应的是Dalvik_dalvik_system_DexFile_defineClass方法。注意defineClass函数传递进来的参数有一个是mCookie,就是在DexClassLoader和PathClassLoader加载Dex流程一文中,openDexFile生成的,利用这个mCookie可以在native层找到openDexFile生成的DexFile结构体。
0x02
Dalvik_dalvik_system_DexFile_defineClass代码位于dalvik\vm\native\dalvik_system_DexFile.c。
static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
JValue* pResult)
{
StringObject* nameObj = (StringObject*) args[0];
Object* loader = (Object*) args[1];
int cookie = args[2];
Object* pd = (Object*) args[3];
ClassObject* clazz = NULL;
DexOrJar* pDexOrJar = (DexOrJar*) cookie;
DvmDex* pDvmDex;
char* name;
char* descriptor;
name = dvmCreateCstrFromString(nameObj);
descriptor = dvmDotToDescriptor(name);
LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie);
free(name);
if (!validateCookie(cookie))
RETURN_VOID();
if (pDexOrJar->isDex)
pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
else
pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
/* once we load something, we can't unmap the storage */
pDexOrJar->okayToFree = false;
clazz = dvmDefineClass(pDvmDex, descriptor, loader);
......
......
free(descriptor);
RETURN_PTR(clazz);
}
首先通过cookie找到DexOrJar结构体pDexOrJar,然后根据pDexOrJar找到DvmDex结构体pDvmDex。
下面我们来分析核心函数dvmDefineClass,这个用来生成ClassObject。dvmDefineClass,findClassNoInit 方法都位于dalvik\vm\oo\Class.c。