1. 程式人生 > >Dalvik學習筆記--啟動過程

Dalvik學習筆記--啟動過程

學習老羅的部落格,順便記點筆記,強化記憶

程式碼用的4.3,與老羅不一樣的地方會標註出來

從AndroidRuntime.start開始

void AndroidRuntime::start(const char* className, const char* options)
{
    ......

    /* start the virtual machine */
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env);<span style="white-space:pre">	</span>//此函式為空函式,原文註釋:If AndroidRuntime had anything to do here, we'd have done it in 'start'.

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;
    jstring optionsStr;

    stringClass = env->FindClass("java/lang/String");<span style="white-space:pre">		
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(2, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);<span style="white-space:pre">			</span>//構造2個元素的String陣列
    optionsStr = env->NewStringUTF(options);<span style="white-space:pre">					</span>//第一個元素為類名
    env->SetObjectArrayElement(strArray, 1, optionsStr);<span style="white-space:pre">			</span>//第二個為設定

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className);<span style="white-space:pre">				</span>//將字串中的 . 替換為 /
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");<span style="white-space:pre">						</span>//獲取main方法的id
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);<span style="white-space:pre">		</span>//呼叫main方法

    ......
}
GetStaticMethodID函式用於獲取方法id,一個引數為目標類,第二個為方法名,第三個是引數描述(是不是很像smali程式碼)

CallStaticVoidMethod函式第一個引數為目標類,第二個為方法id,第三個是可變引數

這裡有個小小的變化就是toSlashClassName將原來的幾行程式碼封裝了一下。

例項建立startVm函式

該函式很長,但是大致可以分為三部分

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    int result = -1;
    JavaVMInitArgs initArgs;
    JavaVMOption opt;
    char propBuf[PROPERTY_VALUE_MAX];
    char stackTraceFileBuf[PROPERTY_VALUE_MAX];
    char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
    char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
    char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
    char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
    char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
    char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];
    char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];
    char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];
    char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
    char extraOptsBuf[PROPERTY_VALUE_MAX];
    char* stackTraceFile = NULL;
    bool checkJni = false;
    bool checkDexSum = false;
    bool logStdio = false;
    enum {
      kEMDefault,
      kEMIntPortable,
      kEMIntFast,
      kEMJitCompiler,
    } executionMode = kEMDefault;
先宣告字串用於存放配置資訊,宣告標誌變數
    property_get("dalvik.vm.checkjni", propBuf, "");
    if (strcmp(propBuf, "true") == 0) {
        checkJni = true;
    } else if (strcmp(propBuf, "false") != 0) {
        /* property is neither true nor false; fall back on kernel parameter */
        property_get("ro.kernel.android.checkjni", propBuf, "");
        if (propBuf[0] == '1') {
            checkJni = true;
        }
    }
    ......
    /* Force interpreter-only mode for selected methods */
    char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
    property_get("dalvik.vm.jit.method", propBuf, "");
    if (strlen(propBuf) > 0) {
        strcpy(jitMethodBuf, "-Xjitmethod:");
        strcat(jitMethodBuf, propBuf);
        opt.optionString = jitMethodBuf;
        mOptions.add(opt);
    }
第二部分大多都是上述這種形式,先用property_get函式,在之前宣告的字串中存放配置資訊,比較後設置對應的標誌變數

比如在propBuf中存放dalvik.vm.checkjni的資訊,如果propBuf字串為“true”,就將checkJni設定為true

    if (executionMode == kEMIntPortable) {
        opt.optionString = "-Xint:portable";
        mOptions.add(opt);
    } else if (executionMode == kEMIntFast) {
        opt.optionString = "-Xint:fast";
        mOptions.add(opt);
    } else if (executionMode == kEMJitCompiler) {
        opt.optionString = "-Xint:jit";
        mOptions.add(opt);
    }

    if (checkDexSum) {
        /* perform additional DEX checksum tests */
        opt.optionString = "-Xcheckdexsum";
        mOptions.add(opt);
    }
    ......
再根據這些標誌變數進行設定。
    /*
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        goto bail;
    }

當然最後呼叫JNI_CreateJavaVM函式。

/*
 * Create a new VM instance.
 *
 * The current thread becomes the main VM thread.  We return immediately,
 * which effectively means the caller is executing in a native method.
 */
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
    const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
    if (args->version < JNI_VERSION_1_2) {
        return JNI_EVERSION;
    }

    // TODO: don't allow creation of multiple VMs -- one per customer for now

    /* zero globals; not strictly necessary the first time a VM is started */
    memset(&gDvm, 0, sizeof(gDvm));

    /*
     * Set up structures for JNIEnv and VM.
     */
    JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
    pVM->funcTable = &gInvokeInterface;
    pVM->envList = NULL;
    dvmInitMutex(&pVM->envListLock);

    UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
    memset(argv.get(), 0, sizeof(char*) * (args->nOptions));

    /*
     * Convert JNI args to argv.
     *
     * We have to pull out vfprintf/exit/abort, because they use the
     * "extraInfo" field to pass function pointer "hooks" in.  We also
     * look for the -Xcheck:jni stuff here.
     */
    int argc = 0;
    for (int i = 0; i < args->nOptions; i++) {<span style="white-space:pre">		</span>//收集虛擬機器相關資訊
        const char* optStr = args->options[i].optionString;
        if (optStr == NULL) {
            dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
            return JNI_ERR;
        } else if (strcmp(optStr, "vfprintf") == 0) {
            gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
        } else if (strcmp(optStr, "exit") == 0) {
            gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
        } else if (strcmp(optStr, "abort") == 0) {
            gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
        } else if (strcmp(optStr, "sensitiveThread") == 0) {
            gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
        } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
            gDvmJni.useCheckJni = true;
        } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
            char* jniOpts = strdup(optStr + 10);
            size_t jniOptCount = 1;
            for (char* p = jniOpts; *p != 0; ++p) {
                if (*p == ',') {
                    ++jniOptCount;
                    *p = 0;
                }
            }
            char* jniOpt = jniOpts;
            for (size_t i = 0; i < jniOptCount; ++i) {
                if (strcmp(jniOpt, "warnonly") == 0) {
                    gDvmJni.warnOnly = true;
                } else if (strcmp(jniOpt, "forcecopy") == 0) {
                    gDvmJni.forceCopy = true;
                } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {
                    gDvmJni.logThirdPartyJni = true;
                } else {
                    dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
                            jniOpt);
                    return JNI_ERR;
                }
                jniOpt += strlen(jniOpt) + 1;
            }
            free(jniOpts);
        } else {
            /* regular option */
            argv[argc++] = optStr;
        }
    }

    if (gDvmJni.useCheckJni) {
        dvmUseCheckedJniVm(pVM);
    }

    if (gDvmJni.jniVm != NULL) {
        dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
        return JNI_ERR;
    }
    gDvmJni.jniVm = (JavaVM*) pVM;

    /*
     * Create a JNIEnv for the main thread.  We need to have something set up
     * here because some of the class initialization we do when starting
     * up the VM will call into native code.
     */
    JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);

    /* Initialize VM. */
    gDvm.initializing = true;
    std::string status =
            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);<span style="white-space:pre">	</span>//初始化虛擬機器,關鍵函式,其他程式碼都是為他的引數做準備
    gDvm.initializing = false;

    if (!status.empty()) {
        free(pEnv);
        free(pVM);
        ALOGW("CreateJavaVM failed: %s", status.c_str());
        return JNI_ERR;
    }

    /*
     * Success!  Return stuff to caller.
     */
    dvmChangeStatus(NULL, THREAD_NATIVE);
    *p_env = (JNIEnv*) pEnv;
    *p_vm = (JavaVM*) pVM;
    ALOGV("CreateJavaVM succeeded");
    return JNI_OK;
}
程式碼註釋很全,簡單說一下。先給一個虛擬機器例項分配空間,初始化需要切換當前執行緒狀態,需要儲存設定和建立一個執行環境,gDvm就是用來收集虛擬機器資訊的全域性變數(給例項分配的空間也儲存在當中),用於在不同執行緒狀態間傳遞虛擬機器例項,argv儲存從vm_args傳遞過來的引數,通過這個幾個變數就可以切換執行緒狀態建立虛擬機器例項。建立完成後再通過dvmChangeStatus切換回去(Return stuff to caller)。然後將例項(pVM)和環境(pEnv)傳遞給呼叫者。

上述提到的DvmGlobals結構體定義檔案dalvik/vm/Globals.h中,JNIInvokeInterface結構體定義在檔案dalvik/libnativehelper/include/nativehelper/jni.h中,JavaVMExt和JNIEnvExt結構體定義在檔案dalvik/vm/JniInternal.h中。

接下來是dvmCreateJNIEnv函式

JNIEnv* dvmCreateJNIEnv(Thread* self) {
    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;

    //if (self != NULL)
    //    ALOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);

    assert(vm != NULL);

    JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));<span style="white-space:pre">	</span>//建立一個JNIEnvExt物件
    newEnv->funcTable = &gNativeInterface;<span style="white-space:pre">				</span>//設定本地藉口表
    if (self != NULL) {<span style="white-space:pre">							</span>//self表示所要關聯的執行緒
        dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);<span style="white-space:pre">			</span>//設定關聯的函式
        assert(newEnv->envThreadId != 0);
    } else {
        /* make it obvious if we fail to initialize these later */
        newEnv->envThreadId = 0x77777775;<span style="white-space:pre">				</span>//表示還未與執行緒關聯
        newEnv->self = (Thread*) 0x77777779;
    }
    if (gDvmJni.useCheckJni) {
        dvmUseCheckedJniEnv(newEnv);
    }

    ScopedPthreadMutexLock lock(&vm->envListLock);

    /* insert at head of list */
    newEnv->next = vm->envList;<span style="white-space:pre">						</span>//newEnv的宿主虛擬機器是vm,也就是之前建立的例項
    assert(newEnv->prev == NULL);
    if (vm->envList == NULL) {<span style="white-space:pre">						</span>//將newEnv插入到vm->envList連結串列中
        // rare, but possible
        vm->envList = newEnv;
    } else {
        vm->envList->prev = newEnv;
    }
    vm->envList = newEnv;

    //if (self != NULL)
    //    ALOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
    return (JNIEnv*) newEnv;
}
 在一個Dalvik虛擬機器裡面,可以執行多個執行緒。所有關聯有JNI環境的執行緒都有一個對應的JNIEnvExt物件,這些JNIEnvExt物件相互連線在一起儲存在用來描述其宿主Dalvik虛擬機器的一個JavaVMExt物件的成員變數envList中。因此,前面建立的JNIEnvExt物件需要連線到其宿主Dalvik虛擬機器的JavaVMExt連結串列中去。
再看dvmStartup函式
std::string dvmStartup(int argc, const char* const argv[],
        bool ignoreUnrecognized, JNIEnv* pEnv)
{
    ScopedShutdown scopedShutdown;

    assert(gDvm.initializing);

    ALOGV("VM init args (%d):", argc);
    for (int i = 0; i < argc; i++) {
        ALOGV("  %d: '%s'", i, argv[i]);
    }
    setCommandLineDefaults();<span style="white-space:pre">					</span>//設定預設項

    /*
     * Process the option flags (if any).
     */
    int cc = processOptions(argc, argv, ignoreUnrecognized);<span style="white-space:pre">	</span>//處理啟動選項
    if (cc != 0) {
        if (cc < 0) {
            dvmFprintf(stderr, "\n");
            usage("dalvikvm");
        }
        return "syntax error";
    }
與老羅程式碼相比沒有了dvmPropertiesStartup來分配空間
    /*
     * Initialize components.
     */
    dvmQuasiAtomicsStartup();
    if (!dvmAllocTrackerStartup()) {<span style="white-space:pre">	</span>//物件分配記錄子模組
        return "dvmAllocTrackerStartup failed";
    }
    if (!dvmGcStartup()) {<span style="white-space:pre">		</span>//GC子模組
        return "dvmGcStartup failed";
    }
    if (!dvmThreadStartup()) {<span style="white-space:pre">		</span>//執行緒列表
        return "dvmThreadStartup failed";
    }
    if (!dvmInlineNativeStartup()) {<span style="white-space:pre">	</span>//內建Native函式表
        return "dvmInlineNativeStartup";
    }
    if (!dvmRegisterMapStartup()) {<span style="white-space:pre">	</span>//暫存器對映集
        return "dvmRegisterMapStartup failed";
    }
    if (!dvmInstanceofStartup()) {<span style="white-space:pre">	</span>//例項操作符子模組
        return "dvmInstanceofStartup failed";
    }
    if (!dvmClassStartup()) {<span style="white-space:pre">		</span>//啟動類載入器
        return "dvmClassStartup failed";
    }

    /*
     * At this point, the system is guaranteed to be sufficiently
     * initialized that we can look up classes and class members. This
     * call populates the gDvm instance with all the class and member
     * references that the VM wants to use directly.
     */
    if (!dvmFindRequiredClassesAndMembers()) {<span style="white-space:pre">	</span>//重要類和函式
        return "dvmFindRequiredClassesAndMembers failed";
    }

    if (!dvmStringInternStartup()) {<span style="white-space:pre">	</span>//字串池
        return "dvmStringInternStartup failed";
    }
    if (!dvmNativeStartup()) {<span style="white-space:pre">		</span>//so庫載入表
        return "dvmNativeStartup failed";
    }
    if (!dvmInternalNativeStartup()) {<span style="white-space:pre">	</span>//內部Native函式表
        return "dvmInternalNativeStartup failed";
    }
    if (!dvmJniStartup()) {<span style="white-space:pre">		</span>//全域性引用表
        return "dvmJniStartup failed";
    }
    if (!dvmProfilingStartup()) {<span style="white-space:pre">	</span>//效能分析子模組
        return "dvmProfilingStartup failed";
    }

    /*
     * Create a table of methods for which we will substitute an "inline"
     * version for performance.
     */
    if (!dvmCreateInlineSubsTable()) {<span style="white-space:pre">	</span>//行內函數表
        return "dvmCreateInlineSubsTable failed";
    }

    /*
     * Miscellaneous class library validation.
     */
    if (!dvmValidateBoxClasses()) {<span style="white-space:pre">	</span>//驗證虛擬機器中相應的裝箱類
        return "dvmValidateBoxClasses failed";
    }

    /*
     * Do the last bits of Thread struct initialization we need to allow
     * JNI calls to work.
     */
    if (!dvmPrepMainForJni(pEnv)) {<span style="white-space:pre">	</span>//準備主執行緒JNI環境
        return "dvmPrepMainForJni failed";
    }

    /*
     * Explicitly initialize java.lang.Class.  This doesn't happen
     * automatically because it's allocated specially (it's an instance
     * of itself).  Must happen before registration of system natives,
     * which make some calls that throw assertions if the classes they
     * operate on aren't initialized.
     */
    if (!dvmInitClass(gDvm.classJavaLangClass)) {//確保目標類初始化
        return "couldn't initialized java.lang.Class";
    }

    /*
     * Register the system native methods, which are registered through JNI.
     */
    if (!registerSystemNatives(pEnv)) {<span style="white-space:pre">	</span>//為JAVA核心類註冊JNI方法
        return "couldn't register system natives";
    }

    /*
     * Do some "late" initialization for the memory allocator.  This may
     * allocate storage and initialize classes.
     */
    if (!dvmCreateStockExceptions()) {<span style="white-space:pre">	</span>//預建立與記憶體相關的異樣物件
        return "dvmCreateStockExceptions failed";
    }

    /*
     * At this point, the VM is in a pretty good state.  Finish prep on
     * the main thread (specifically, create a java.lang.Thread object to go
     * along with our Thread struct).  Note we will probably be executing
     * some interpreted class initializer code in here.
     */
    if (!dvmPrepMainThread()) {<span style="white-space:pre">		</span>//為主執行緒建立ThreadGroup物件
        return "dvmPrepMainThread failed";
    }

    /*
     * Make sure we haven't accumulated any tracked references.  The main
     * thread should be starting with a clean slate.
     */
    if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)<span style="white-space:pre">	</span>//確保主執行緒當前不應用JAVA物件,保證一個乾淨的入口
    {
        ALOGW("Warning: tracked references remain post-initialization");
        dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
    }

    /* general debugging setup */
    if (!dvmDebuggerStartup()) {<span style="white-space:pre">	</span>//初始化除錯環境
        return "dvmDebuggerStartup failed";
    }

    if (!dvmGcStartupClasses()) {<span style="white-space:pre">	</span>// GC class
        return "dvmGcStartupClasses failed";
    }
初始化各項子模組
    /*
     * Init for either zygote mode or non-zygote mode.  The key difference
     * is that we don't start any additional threads in Zygote mode.
     */
    if (gDvm.zygote) {
        if (!initZygote()) {
            return "initZygote failed";
        }
    } else {
        if (!dvmInitAfterZygote()) {
            return "dvmInitAfterZygote failed";
        }
    }
判斷是否在zygote中啟動虛擬機器,注意是initZygote不是dvmInitZygote
/*
 * Do zygote-mode-only initialization.
 */
static bool initZygote()
{
    /* zygote goes into its own process group */
    setpgid(0,0);

    // See storage config details at http://source.android.com/tech/storage/
    // Create private mount namespace shared by all children
    if (unshare(CLONE_NEWNS) == -1) {
        SLOGE("Failed to unshare(): %s", strerror(errno));
        return -1;
    }

    // Mark rootfs as being a slave so that changes from default
    // namespace only flow into our children.
    if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {
        SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));
        return -1;
    }

    // Create a staging tmpfs that is shared by our children; they will
    // bind mount storage into their respective private namespaces, which
    // are isolated from each other.
    const char* target_base = getenv("EMULATED_STORAGE_TARGET");
    if (target_base != NULL) {
        if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
                "uid=0,gid=1028,mode=0050") == -1) {
            SLOGE("Failed to mount tmpfs to %s: %s", target_base, strerror(errno));
            return -1;
        }
    }

    // Mark /system as NOSUID | NODEV
    const char* android_root = getenv("ANDROID_ROOT");

    if (android_root == NULL) {
        SLOGE("environment variable ANDROID_ROOT does not exist?!?!");
        return -1;
    }

    std::string mountDev(getMountsDevDir(android_root));
    if (mountDev.empty()) {
        SLOGE("Unable to find mount point for %s", android_root);
        return -1;
    }

    if (mount(mountDev.c_str(), android_root, "none",
            MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_RDONLY | MS_BIND, NULL) == -1) {
        SLOGE("Remount of %s failed: %s", android_root, strerror(errno));
        return -1;
    }

#ifdef HAVE_ANDROID_OS
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        if (errno == EINVAL) {
            SLOGW("PR_SET_NO_NEW_PRIVS failed. "
                  "Is your kernel compiled correctly?: %s", strerror(errno));
            // Don't return -1 here, since it's expected that not all
            // kernels will support this option.
        } else {
            SLOGW("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
            return -1;
        }
    }
#endif

    return true;
}
註釋比較全,主要是父子程序共享資源的問題。

到這裡例項的建立和初始化工作就算完成了,dvmStartup函式也算結束了,要注意的是dvmStartup函式反回的是一個字串。

JNI_CreateJavaVM函式中是這樣呼叫的

    std::string status =
            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
    if (!status.empty()) {
        free(pEnv);
        free(pVM);
        ALOGW("CreateJavaVM failed: %s", status.c_str());
        return JNI_ERR;
    }
返回到AndroidRuntime::start,之後執行的函式是startReg,註冊Android核心類的JNI方法
int AndroidRuntime::startReg(JNIEnv* env)
{
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");

    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     */
    env->PushLocalFrame(200);

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);

    //createJavaThread("fubar", quickTest, (void*) "hello");

    return 0;
}
總結:建立例項,處理配置資訊 ——> 收集配置資訊,建立環境 ——> 切換執行緒狀態,初始化虛擬機器例項 ——> 註冊核心方法 ——> 啟動main方法

最後附上老羅的圖