Art虚拟机preload jar包流程

2020-04-15  本文已影响0人  黑石_2412

        从本质上说,Android系统其实就是运行在Linux内核上的一套虚拟机环境。基本上还是遵循了Linux的游戏规则。第一个进程就是从init.rc开始由Linux初始化完成,后续的进程都是从这个父进程fork出来的,也天然的复制了父进程的资源。这也是为什么Android的第一个进程会是systemserver的原因。

        从Android Application开发角度来说,代码开发所需要的大部分的系统资源,都存放在framework.jar中,当然也有例外,比如我们自己编写的jar,或者其它第三方jar。

        我们在使用这些第三方jar的时候基本上都是直接放在工程目录下编译打包,或者使用classloader动态加载。但是这样有一个弊端,这样的方式只能让这个jar被这一个Application使用,或者使用起来过于麻烦,例如使用classloader动态加载,需要在每一个Application添加大量的额外代码。看上去并不优雅。

        那么我们再换一个思路来看这个问题,在Android中每一个Application都是一个虚拟机进程,都是从第一个父进程fork出来的,而且都复制了SystemServer资源,而且我们也知道system server是被变成framework.jar service.jar的,那也就是说,这两个jar肯定是被虚拟机加载了而且这个过程是不受具体的Application控制的。都是jar,我们的jar同样也可以如此操作。

        那我们就来研究一下,虚拟机是怎么加载了这些jar。

首先回顾一下整个系统的启动流程

Zygote -> system/core/rootdir/init.rc ->frameworks/base/cmds/app_process/app_main.cpp->frameworks/base/core/jni/AndroidRuntime.cpp

        在AndroidRuntime中会调用AndroidRuntime::start(),它主要做了三件事情,

一是调用函数startVM启动虚拟机,

二是调用函数startReg注册JNI方法,

三是调用了com.android.internal.os.ZygoteInit类的main函数。

        这里我们主要关注第一个startVM(),这个函数在设置了一系列配置配置参数后,调用JNI_CreateJavaVM(art\runtime\jni_internal.cc)启动了一个VM虚拟机,在这个函数里又会调用Runtime::Create(options, ignore_unrecognized)(art\runtime\runtime.cc),在Create函数里会继续调用instance_->Init(options, ignore_urecognized),这个instance是Runtime的单例。在函数Init中会继续调用ParsedOptions::Create(raw_options, ignore_unrecognized)(art\runtime\parsed_options.cc),在这个Create函数里会接着调用parsed->Parse(options, ignore_unrecognized),这个parsed就是一个ParsedOptions指针。在这个Parse函数里我们会看到这段代码const char* boot_class_path_string = getenv("BOOTCLASSPATH"); 这个 boot_class_path_string就是虚拟机启动时preload的库文件路径。

整体流程如下

AndroidRuntime::startVM ->jni_internal.cc\JNI_CreateJavaVM->Runtime::Create->Runtime::Init->ParsedOptions::Create->ParsedOptions::Parse

这就很清楚了,如果我们想要预加载我们的jar,只需要修改BOOTCLASSPATH这个全局环境变量的值即可。

在system\core\rootdir这个目录下不光定义了init.rc系统启动脚本,还定义了init.environ.rc.in,这个文件里存放了启动时需要的环境变量

export BOOTCLASSPATH %BOOTCLASSPATH%

这个%BOOTCLASSPATH%是在system\core\rootdir\Android.mk这个文件中赋值

$(hide)sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g'\

这个PRODUCT_BOOTCLASSPATH具体赋值是在build/core/dex_preopt.mk文件中

# list of boot classpath jars for dexpreopt

DEXPREOPT_BOOT_JARS := $(subst $(space),:,$(PRODUCT_BOOT_JARS))

DEXPREOPT_BOOT_JARS_MODULES := $(PRODUCT_BOOT_JARS)

PRODUCT_BOOTCLASSPATH := $(subst $(space),:,$(foreach m,$(DEXPREOPT_BOOT_JARS_MODULES),/system/framework/$(m).jar))

PRODUCT_BOOT_JARS的赋值又是在文件build\target\product\core_minimal.mk完成的

# The order of PRODUCT_BOOT_JARS matters.

PRODUCT_BOOT_JARS :=\

    core-libart \

    conscrypt \

    okhttp \

    core-junit \

    bouncycastle \

    ext \

    framework \

    telephony-common \

    voip-common \

    ims-common \

    mms-common \

    android.policy \

    apache-xml \

至此我们应该就知道我们需要把在哪些配置文件里添加我们自己的jar,以及把我们的jar放置到哪些文件目录下。

上一篇下一篇

猜你喜欢

热点阅读