涉及源碼(Android 4.4.2):
/dalvik/vm/Init.cpp
/dalvik/vm/oo/Class.cpp
在Dalvik虛擬機啟動過程中,我們并沒有對初始化并啟動虛擬機的過程也就是dvmStartup函數(shù)展開分析,這里我們對其中BOOTCLASSPATH類加載過程進行分析,也就是dvmClassStartup函數(shù),下面我們再來看看dvmStartup函數(shù)。
/dalvik/vm/Init.cpp
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
...
ALOGV("VM init args (%d):", argc);
...
// 1、讀取BOOTCLASSPATH
setCommandLineDefaults();
// 2、初始化bootstrap class loader
if (!dvmClassStartup()) {
return "dvmClassStartup failed";
}
}
1、讀取BOOTCLASSPATH
static void setCommandLineDefaults()
{
const char* envStr = getenv("CLASSPATH");
if (envStr != NULL) {
gDvm.classPathStr = strdup(envStr);
} else {
gDvm.classPathStr = strdup(".");
}
// 讀取到BOOTCLASSPATH環(huán)境變量,就可以拿到BOOTCLASSPATH路徑
envStr = getenv("BOOTCLASSPATH");
if (envStr != NULL) {
gDvm.bootClassPathStr = strdup(envStr);
} else {
gDvm.bootClassPathStr = strdup(".");
}
...
}
從上面可以知道,BOOTCLASSPATH路徑的值被保存到gDvm.bootClassPathStr中。
2、初始化bootstrap class loader
這個函數(shù)定義在文件dalvik/vm/oo/Class.c中,用來初始化啟動類加載器(Bootstrap Class Loader),同時還會初始化java.lang.Class類。啟動類加載器是用來加載Java核心類的,用來保證安全性,即保證加載的Java核心類是合法的。
/dalvik/vm/oo/Class.cpp
bool dvmClassStartup()
{
/* make this a requirement -- don't currently support dirs in path */
if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
ALOGE("ERROR: must specify non-'.' bootclasspath");
return false;
}
gDvm.loadedClasses =
dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
if (gDvm.pBootLoaderAlloc == NULL)
return false;
if (false) {
linearAllocTests();
exit(0);
}
gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
gDvm.initiatingLoaderList = (InitiatingLoaderList*)
calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
// createInitialClasses加載了9大基本類型
if (!createInitialClasses()) {
return false;
}
/*
* Process the bootstrap class path. This means opening the specified
* DEX or Jar files and possibly running them through the optimizer.
*/
assert(gDvm.bootClassPath == NULL);
// 加載所有的Boot Class
processClassPath(gDvm.bootClassPathStr, true);
if (gDvm.bootClassPath == NULL)
return false;
return true;
}
下面來看看processClassPath方法加載所有的Boot Class的過程。
static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap)
{
ClassPathEntry* cpe = NULL;
char* mangle;
char* cp;
const char* end;
int idx, count;
/*
* Allocate storage. We over-alloc by one so we can set an "end" marker.
*/
cpe = (ClassPathEntry*) calloc(count+1, sizeof(ClassPathEntry));
/*
* Set the global pointer so the DEX file dependency stuff can find it.
*/
// 初始化bootClassPath,它是以全局的指針,指向所有依賴的jar
gDvm.bootClassPath = cpe;
/*
* Go through a second time, pulling stuff out.
*/
cp = mangle;
idx = 0;
while (cp < end) {
if (*cp == '\0') {
/* leading, trailing, or doubled ':'; ignore it */
} else {
if (isBootstrap &&
dvmPathToAbsolutePortion(cp) == NULL) {
ALOGE("Non-absolute bootclasspath entry '%s'", cp);
free(cpe);
cpe = NULL;
goto bail;
}
ClassPathEntry tmp;
tmp.kind = kCpeUnknown;
tmp.fileName = strdup(cp);
tmp.ptr = NULL;
/*
* Drop an end marker here so DEX loader can walk unfinished
* list.
*/
cpe[idx].kind = kCpeLastEntry;
cpe[idx].fileName = NULL;
cpe[idx].ptr = NULL;
if (!prepareCpe(&tmp, isBootstrap)) {
/* drop from list and continue on */
free(tmp.fileName);
} else {
/* copy over, pointers and all */
cpe[idx] = tmp;
idx++;
}
}
cp += strlen(cp) +1;
}
assert(idx <= count);
if (idx == 0 && !gDvm.optimizing) {
/*
* There's no way the vm will be doing anything if this is the
* case, so just bail out (reasonably) gracefully.
*/
ALOGE("No valid entries found in bootclasspath '%s'", pathStr);
gDvm.lastMessage = pathStr;
dvmAbort();
}
LOGVV(" (filled %d of %d slots)", idx, count);
/* put end marker in over-alloc slot */
cpe[idx].kind = kCpeLastEntry;
cpe[idx].fileName = NULL;
cpe[idx].ptr = NULL;
//dumpClassPath(cpe);
bail:
free(mangle);
gDvm.bootClassPath = cpe;
return cpe;
}
BOOTCLASSPATH包含多個文件,這里的處理方式就是將每一個jar都封裝成一個ClassPathEntry對象,每一個ClassPathEntry對象都指向一個jar包。
static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap)
{
struct stat sb;
if (stat(cpe->fileName, &sb) < 0) {
ALOGD("Unable to stat classpath element '%s'", cpe->fileName);
return false;
}
if (S_ISDIR(sb.st_mode)) {
ALOGE("Directory classpath elements are not supported: %s", cpe->fileName);
return false;
}
char suffix[10];
getFileNameSuffix(cpe->fileName, suffix, sizeof(suffix));
if ((strcmp(suffix, "jar") == 0) || (strcmp(suffix, "zip") == 0) ||
(strcmp(suffix, "apk") == 0)) {
JarFile* pJarFile = NULL;
if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) {
cpe->kind = kCpeJar;
cpe->ptr = pJarFile;
return true;
}
} else if (strcmp(suffix, "dex") == 0) {
RawDexFile* pRawDexFile = NULL;
if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0) {
cpe->kind = kCpeDex;
cpe->ptr = pRawDexFile;
return true;
}
} else {
ALOGE("Unknown type suffix '%s'", suffix);
}
ALOGD("Unable to process classpath element '%s'", cpe->fileName);
return false;
}
上面代碼應(yīng)該很熟悉了,就是加載dex或者jar的過程。在文章Dalvik虛擬機對dex的加載過程中分析過。
上面主要工作就是:
(1)對于.jar/.zip/.apk結(jié)尾的文件,則調(diào)用dvmJarFileOpen進行處理。
對于.dex結(jié)尾的文件則調(diào)用dvmRawDexFileOpen進行處理。
(2)處理成功后,則設(shè)置ClassPathEntry的kind為KCpeJar或者是KCpeDex,代表文件的類型是Jar還是Dex。并且設(shè)置cpe->ptr指針為對應(yīng)的文件(jar文件則是JarFile,Dex文件這是RawDexFile)
整個過程的關(guān)系圖如下:
參考文章:
http://www.cnblogs.com/jacobchen/p/3599483.html
http://www.infoq.com/cn/articles/android-in-depth-dalvik
http://blog.csdn.net/luoshengyang/article/details/8885792