当在虚拟机里加载Dex文件后,这个文件的数据已经读取到内存里,能不能马上使用呢?能不能使用里面的类呢?显然是不行的,因为那些加载到内存的数据,只是储存的格式,不具备运行的条件,因此需要调用方法defineClass来定义类,才可以运行在虚拟机里。所有Java编译后的类保存在Dex文件里,使用上面介绍的方法openDexFile打开Dex文件,接着需要调用方法defineClass来定义类,其实就是调用原生的函数Dalvik_dalvik_system_DexFile_defineClass, 类的加载流程如下:
1)方法openDexFile里通过dvmDexFileOpenFromFd函数调用dexFileParse函数,分析Dex文件里每个类名称和类的代码所在索引,然后dexFileParse调用函数dexParseOptData来把类名称写对象pDexFile->pClassLookup里面,当然也更新了索引。
2)Dalvik_dalvik_system_DexFile_defineClass函数调用dvmDefineClass函数。
3)dvmDefineClass函数调用findClassNoInit来进类的初始化。
4)findClassNoInit调用dexFindClass找到类名称在文件中位置。
5)findClassNoInit调用loadClassFromDex从文件加载。
6)loadClassFromDex调用dexGetClassData函数获取类的索引地址。
7)loadClassFromDex调用loadClassFromDex0获取类接口、类成员变量、类的方法,这样就完成类从Dex文件里加载。
下面来依次分析调用的函数代码,理解其中细节,以及相应的代码,以便更加深入,达到修改代码的水平,Dalvik_dalvik_system_DexFile_defineClass函数代码如下:
staticvoid Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
JValue* pResult)
{
StringObject* nameObj = (StringObject*)args[0];
这行代码是获取输入需要加载的类对象。
Object* loader = (Object*) args[1];
这行代码是获取类加载器对象。
int cookie = args[2];
这行代码是获取打开的Dex文件对象。
Object* pd = (Object*) args[3];
这行代码是获取保护域权限。
ClassObject* clazz = NULL;
DexOrJar* pDexOrJar = (DexOrJar*) cookie;
这行代码转换为Dex文件对象。
DvmDex* pDvmDex;
char* name;
char* descriptor;
name = dvmCreateCstrFromString(nameObj);
descriptor = dvmDotToDescriptor(name);
LOGV("--- Explicit class load '%s'0x%08x\n", descriptor, cookie);
free(name);
这段代码是把类名称对象转换为C字符串,然后从C字符串转换为类描述符。
if (!validateCookie(cookie))
RETURN_VOID();
这段代码是检查是否合法的Dex文件对象。
if (pDexOrJar->isDex)
pDvmDex =dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
else
pDvmDex =dvmGetJarFileDex(pDexOrJar->pJarFile);
这段代码是获取Dex文件对象,以便从Dex文件里获取到类数据。
/* once we load something, we can't unmapthe storage */
pDexOrJar->okayToFree = false;
clazz = dvmDefineClass(pDvmDex, descriptor,loader);
这行代码是调用函数dvmDefineClass从指定的Dex文件里,使用指定的类加载器加载指定类名称的类。
Thread* self = dvmThreadSelf();
if (dvmCheckException(self)) {
/*
* If we threw a "class not found"exception, stifle it, since the
* contract in the higher method says wesimply return null if
* the class is not found.
*/
Object* excep = dvmGetException(self);
if (strcmp(excep->clazz->descriptor,
"Ljava/lang/ClassNotFoundException;")== 0 ||
strcmp(excep->clazz->descriptor,
"Ljava/lang/NoClassDefFoundError;")== 0)
{
dvmClearException(self);
}
clazz = NULL;
}
这段代码是检查是否加载类的过程有异常发生。
/*
* Set the ProtectionDomain -- do we needthis to happen before we
* link the class and make it available? Ifso, we need to pass it
* through dvmDefineClass (and figure outsome other
* stuff, like where it comes from forbootstrap classes).
*/
if (clazz != NULL) {
//LOGI("SETTING pd '%s' to %p\n",clazz->descriptor, pd);
dvmSetFieldObject((Object*) clazz,gDvm.offJavaLangClass_pd, pd);
}
这段代码是设置类的保护域。
free(descriptor);
RETURN_PTR(clazz);
这行代码返回已经加载的类代码。
}