Android系统启动流程-ART虚拟机的启动


启动JVM的通用流程

  1. 加载虚拟机实现的动态链接库(Android中是so文件);

    一般通过dlopen方法加载so文件.

  2. 设置启动参数,并使用JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*)方法创建虚拟机;

这个时候就可以执行Java程序了.

我这里用C++写了个简单的例子,用来创建JVM:

C++创建虚拟机

#include <iostream>
#include <jni.h>
#include <dlfcn.h>

int main() {
    auto jvmHandler = dlopen(
            "../libjvm.dylib",
            RTLD_NOW | RTLD_NODELETE);
    if (jvmHandler == nullptr) {
        std::cerr << "load libjvm.dylib failed" << std::endl;
    }
    JavaVM *javaVm;
    JNIEnv *jniEnv;
    JavaVMInitArgs vmInitArgs;
    JavaVMOption *options = new JavaVMOption[2];
    options[0].optionString = "-Djava.class.path=.";
    options[1].optionString = "-version";
    vmInitArgs.version = JNI_VERSION_1_4;
    vmInitArgs.nOptions = 1;
    vmInitArgs.options = options;
    vmInitArgs.ignoreUnrecognized = JNI_TRUE;
    auto status = JNI_CreateJavaVM(&javaVm, (void **) &jniEnv, &vmInitArgs);
    if (status < 0 || javaVm == nullptr || jniEnv == nullptr) {
        std::cerr << "Create JavaVM failed!" << std::endl;
        return 0;
    }
    delete (options);
    jclass cls = jniEnv->FindClass("Main");
    if (jniEnv->ExceptionCheck() == JNI_TRUE || cls == nullptr) {
        jniEnv->ExceptionDescribe();
        jniEnv->ExceptionClear();
        free(jvmHandler);
        std::cerr << "find class Main failed!" << std::endl;
        return 0;
    }
    jmethodID mehtodTestId = jniEnv->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
    jniEnv->CallStaticVoidMethod(cls, mehtodTestId, nullptr);
    javaVm->DestroyJavaVM();
    return 0;
}

代码没什么好说的,按照指定的流程加载就行,实际运用的时候启动参数得设置很多.

在使用app_process创建并启动zygote进程之前,会先启动虚拟机.

在AndroidRuntime::start()方法中有这么一段代码:

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

JniInvocation其实是一个中间层,通过这个中国层,那些使用JNI invocation API的应用可以动态选择JNI的实现方式.在Android中就是动态选择Dalvik虚拟机和ART虚拟机(可通过persist.sys.dalvik.vm.lib.2指定)

  bool Init(const char* library) {
    return JniInvocationInit(impl_, library) != 0;
  }

libnativehelper/JniInvocation.c中:

// 默认库
static const char* kDefaultJniInvocationLibrary = "libart.so";

bool JniInvocationInit(struct JniInvocationImpl* instance, const char* library_name) {
#ifdef __ANDROID__
  char buffer[PROP_VALUE_MAX];
#else
  char* buffer = NULL;
#endif
  // 获取虚拟机库名,这里是libart.so
  library_name = JniInvocationGetLibrary(library_name, buffer);
  // 使用dlopen加载虚拟机库
  DlLibrary library = DlOpenLibrary(library_name);
  if (library == NULL) {
    if (strcmp(library_name, kDefaultJniInvocationLibrary) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library_name, DlGetError());
      return false;
    }
    // Note that this is enough to get something like the zygote
    // running, we can't property_set here to fix this for the future
    // because we are root and not the system user. See
    // RuntimeInit.commonInit for where we fix up the property to
    // avoid future fallbacks. http://b/11463182
    ALOGW("Falling back from %s to %s after dlopen error: %s",
          library_name, kDefaultJniInvocationLibrary, DlGetError());
    library_name = kDefaultJniInvocationLibrary;
    library = DlOpenLibrary(library_name);
    if (library == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, DlGetError());
      return false;
    }
  }

    // 查找对应JNI接口
  DlSymbol JNI_GetDefaultJavaVMInitArgs_ = FindSymbol(library, "JNI_GetDefaultJavaVMInitArgs");
  if (JNI_GetDefaultJavaVMInitArgs_ == NULL) {
    return false;
  }

  DlSymbol JNI_CreateJavaVM_ = FindSymbol(library, "JNI_CreateJavaVM");
  if (JNI_CreateJavaVM_ == NULL) {
    return false;
  }

  DlSymbol JNI_GetCreatedJavaVMs_ = FindSymbol(library, "JNI_GetCreatedJavaVMs");
  if (JNI_GetCreatedJavaVMs_ == NULL) {
    return false;
  }

  instance->jni_provider_library_name = library_name;
  instance->jni_provider_library = library;
  instance->JNI_GetDefaultJavaVMInitArgs = (jint (*)(void *)) JNI_GetDefaultJavaVMInitArgs_;
  instance->JNI_CreateJavaVM = (jint (*)(JavaVM**, JNIEnv**, void*)) JNI_CreateJavaVM_;
  instance->JNI_GetCreatedJavaVMs = (jint (*)(JavaVM**, jsize, jsize*)) JNI_GetCreatedJavaVMs_;

  return true;
}

这里默认就是libart.so,然后使用dlopen系统调用进行加载,查找主要的JNI接口.然后将接口的实现切换为ART虚拟机.

在startVM中,首先会初始化大量的启动参数,然后就调用JNI_CreateJavaVM()方法创建ART虚拟机:

    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    /*
     * 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");
        return -1;
    }

这个时候ART虚拟机就创建成功了.


文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
Android系统启动流程-桌面程序的启动 Android系统启动流程-桌面程序的启动
在Android启动流程-SystemServer分析中有提到,在SystemServer启动“Other”类型服务的时候,最后阶段会执行ActivityManagerService的systemReady()方法,这里就是启动桌面程序的入
2020-06-29
下一篇 
Android中的Deep Link与 APP Link Android中的Deep Link与 APP Link
Deep link其实就是一种intent过滤器.可以直接进入APP特定的Activity. 如果APP中存在多个符合要求的Activity,会弹出一个选择框. App link一种特殊的deep link,需要验证网站网址. 用一
2020-06-28
  目录