Android(安卓)ART模式简介
新书上市《深入解析Android 5.0系统》
以下内容节选自本书
ART和dalvik相比,系统的性能得到了显著提升,同时占用的内存更少,因此能支持配置更低的设备。但是ART模式下编译出来的文件会比以前增大10%-20%,系统需要更多的存储空间,同时因为在安装时要执行编译,应用的安装时间也比以前更长了。
ART和dalvik相比,系统的性能得到了显著提升,同时占用的内存更少,因此能支持配置更低的设备。但是ART模式下编译出来的文件会比以前增大10%-20%,系统需要更多的存储空间,同时因为在安装时要执行编译,应用的安装时间也比以前更长了。
开启ART模式
在Android中ART模式缺省是关闭的,如果我们希望启动ART模式,需要在“设置”应用中手工打开它。进入“设置”后,选择“开发者选项”,再点击“选择运行环境”,会弹出如选择DALVIK或ART的对话框。选择ART后,系统将会重新启动,然后系统的运行环境就会从Dalvik切换到ART模式。
让我们打开“设置”应用的代码,看看这种切换到底做了些什么。下面是DevelopmentSettings.java中的一段代码:
builder.setPositiveButton(android.R.string.ok, newOnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SystemProperties.set(SELECT_RUNTIME_PROPERTY,newRuntimeValue);
pokeSystemProperties();
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
pm.reboot(null);
}
});
上面这段代码只是设置重新设置了属性SELECT_RUNTIME_PROPERTY的值,然后就重启设备。这个属性的定义如下:
String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib"
注意属性是以persist开头的,所以它的值在重新开机后也可以保存。这个属性定义的是当前使用的运行环境库,如果是dalvik,它的值为libdvm.so,如果是art,它的值为libart.so。通过这个属性,系统就能判断出需要运行的模式。
ART系统的源码位于目录art下,包含了以下目录:
build目录:存放了一些mk文件。
compiler目录:动态库libart-compiler.so的源码目录,它包含了一个ART的编译器。
dalvikvm目录:可执行文件dalvikvm的源码目录。
dex2oat目录:可执行文件dex2oat的源码目录。用来将dex格式的文件编译成可执行文件。dex2oat文件会链接libart-compiler动态库。
jdwpspy目录:可执行文件jdwpspy的源码目录。这是一个调试工具,通过socket接收VM中输出的消息并打印输出。
oatdump目录:可执行文件oatdump的源码目录。这是一个工具,能dump出art文件的信息。
runtime目录:动态库libart.so的源码目录。
test目录:包含了大量测试代码的目录。
tools目录:包含了几个脚本文件。
两种模式的区别
从系统的实现角度看,ART和Dalvik的区别只表现在两个地方。一处是在安装应用执行优化时,Dalvik模式下执行的是dexopt程序,而ART模式下执行的是dex2oat程序。这个在“11.5.5节dexopt(优化)命令”中已经介绍过来。通过dex2oat程序编译后得到的是elf格式的可执行文件,而不是以前的dalvik字节码了。
另一处是在运行应用时,Dalvik模式下系统为应用进程链接的是libdvm.so,而ART模式下链接的是libart.so。这两个库文件能够相互替换,是因为它们都实现了相同的接口。下面我们看看Android是如何来装载不同运行库的。
前面我们已经介绍过了,Zygote进程启动时会调用AndroidRuntime的start()函数来启动虚拟机,start()函数中有下面两行代码:
void AndroidRuntime::start(const char* className, const char*options)
{
......
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
......
}
我们看看jni_invocation的Init()函数的代码:
bool JniInvocation::Init(const char* library) {
#ifdef HAVE_ANDROID_OS
char default_library[PROPERTY_VALUE_MAX]; //从property中得到运行库的名称
property_get(kLibrarySystemProperty,default_library, kLibraryFallback);
#else
const char* default_library =kLibraryFallback;
#endif
if (library == NULL) {
library =default_library;
}
handle_ = dlopen(library,RTLD_NOW);// 装载运行库
if (handle_ == NULL) {
...... //错误处理
}
// 装载三个VM的入口函数。
if(!FindSymbol(reinterpret_cast(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
returnfalse;
}
if(!FindSymbol(reinterpret_cast(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
returnfalse;
}
if(!FindSymbol(reinterpret_cast(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
returnfalse;
}
return true;
}
JniInvocation的Init()函数中通过属性persist.sys.dalvik.vm.lib取得了运行库的名称,然后调用dlopen()来装载运行库,同时从动态库中读取了三个函数的地址,它们是JNI_GetDefaultJavaVMInitArgs(),JNI_CreateJavaVM()和JNI_GetCreatedJavaVMs()。这三个函数相当于是虚拟机的三个入口函数,通过调用它们,就能创建出虚拟机系统。
更多相关文章
- C语言函数的递归(上)
- [置顶] Android事件总线还能怎么玩?
- Mac下配置Android(安卓)Stuido的HTTP Proxy
- Android(安卓)通知使用权(NotificationListenerService)的使用
- Android(安卓)实例剖析之 notepad
- Android权限问题:Permission is only granted to system apps
- Android中把文件存放在SDCard
- 【Android】Android(安卓)Studio中新创建的app目录结构
- Android开机自动启动Wi-Fi热点的方法