ODEX简介
什么是ODEX?
如果你的apk文件或者jar包没有做过odex优化的话,打开你的文件,你一定会找到一个名字叫做classes.dex的文件(通常情况下,我们看到的实际上是一个以.apk为后缀名的压缩文件,classes.dex文件包含在压缩文件中),这个就是我们的java源码经过ADT编译之后转换成的可执行文件。
对于使用Dalvik虚拟机的Android版本,当系统第一次启动时,会对.apk/.jar文件里的classes.dex文件进行一次“翻译”,“翻译”的过程也就是守护进程installd的函数dexopt来对dex字节码进行优化,实际上也就是将它转化为.odex文件,最终odex文件被保存在手机的VM缓存目录/data/dalvik-cache下(注意这里.odex文件名字仍是.dex后缀,格式如:system@priv-app@Settings@Settings.apk@classes.dex)。
对于使用ART虚拟机的Android版本,首次进系统时,调用/system/bin/dexopt工具来将.apk/.jar里的classes.dex文件翻译成本地机器码,同样保存在/data/dalvik-cache下。
无论是dalvik里对classes.dex进行优化,还是art里对classes.dex进行翻译成本地机器码,最终得到的结果都保存在相同名称的odex文件里,但是前者实际是一个dey文件(表示优化过的dex文件),而后者是一个oat文件(实际上是自定义的elf文件,里面全是机器码)。简单来说,无论是DVM,还是ART,优化的结果都是一个odex文件,只是这两种odex文件有着本质区别(一个是dey字节码,一个是oat机器码)。之所以这么设计,主要通过这种方式,原来任何通过绝对路径引用了该odex文件的代码就都不需要修改了,可以理解为这是ART与Dalvik兼容的结果。所以首次开机时,启动比较慢,就是因为系统在做这些事情,预置的APK越多,启动时间就越长。
如果将这件事情,在编译系统时就做好,装到image里去,那么首次开机的时间,就会节约很多,不过这样做也会让生成的image变得比以前大。
什么是dalvik-cache?
当Android启动时,DVM监控所有程序(APK文件)和框架,并且为他们创建一个依存关系树。DVM通过这个依存关系树来为每个程序优化代码并存储在Dalvik缓存中。这样,所有程序在运行时都会使用优化过的代码。这就是当你刷一个新ROM时,有时候第一次启动时间非常长的原因。当一个程序(或框架库)发生变更,DVM将会重新优化代码并且再次将其保存在缓存中。
/cache/dalvik-cache存放的是system上的程序生成的dex文件,/data/dalvik-cache存放的是/data/app生成的dex文件。
ODEX优化有什么用?
ODEX的用途是分离程序资源和可执行文件、以及做预编译处理,达到加快软件加载速度和开机速度的目的。一般来说,厂商的原厂系统都会为自己的ROM做ODEX优化处理以提高性能。
很多人都会疑问,Android5.0开始,默认已经使用ART虚拟机,弃用Dalvik了。应用程序会在安装时被编译成OAT文件(ART上运行的格式),ODEX还有什么用呢?这里我们先看下Google的回答:
Dex file compilation uses a tool called dex2oat and takes more time than dexopt.
The increase in time varies, but 2-3x increases in compile time are not unusual.
For example, apps that typically take a second to install using dexopt might take 2-3 seconds.
DEX转换成OAT的这个过程是5.0以上系统用户在安装程序或是刷入ROM、增量更新后首次启动时必然执行的。按照Google的说法,相比做过ODEX优化,没有做过优化的DEX转换成OAT需要花费更长的事件,一般是2-3倍。比如安装一个ODEX优化过的程序假设需要1秒钟,未做过优化的程序就需要2~3秒。
由此可见,虽然Dalvik被弃用了,但ODEX优化在Android5.0系统以上依旧起着作用。
编译时ODEX 相关变量
这里简单介绍一下Android编译时,odex优化相关的一些编译选项,可以针对自己的的需要,设置自己系统的这些编译选项,以达到不同的目的。
WITH_DEXPREOPT
这个变量相当于编译时odex优化的总开关,只要想在编译时生成odex文件,就必须设置为true。开启该参数后,会对APK、JAR以及内核镜像进行优化。其中,针对APK、JAR的最直观的优化体现就是,程序的dex被转换成odex。
不过这里需要注意的是打开WITH_DEXPREOPT 宏之后,预编译时提取Odex会增加一定的空间,预置太多apk,会导致system.img 过大,而编译不过。遇到这种情况可以通过删除apk中的dex文件、调大system.img的大小限制,或在预编译时跳过一些apk的odex提取。
WITH_DEXPREOPT_BOOT_IMG_ONLY
在WITH_DEXPREOPT:=true的情况下,若同时设置WITH_DEXPREOPT_BOOT_IMG_ONLY为true,则编译时只对PRODUCT_BOOT_JARS变量里定义的jar包进行odex优化,生成boot.oat和boot.art。
DONT_DEXPREOPT_PREBUILTS
打开这个变量后,编译时不会对Android.mk中包含了include$(BUILD_PREBUILT)的Apk进行odex优化。对于一些以后需要自升级的apk这样做,可以节省空间。
LOCAL_DEX_PREOPT
可在各个Android.mk文件里单独设置这个变量,它只会影响当前Android.mk编译的单个jar包或者apk文件。当设置为true时,classes.dex文件将从.apk/.jar文件中删除;设置成
nostripping,classes.dex文件仍保留在.apk/.jar文件里。
DEX_PREOPT_DEFAULT
当WITH_DEXPREOPT:=true时,LOCAL_DEX_PREOPT如果没有进行单独设置,那么它的值就是DEX_PREOPT_DEFAULT,所以可以通过设置DEX_PREOPT_DEFAULT的值,统一改变所有的LOCAL_DEX_PREOPT的默认值。
一个常见的场景是,DEX_PREOPT_DEFAULT:=nostripping,导致最后生成的image里同时存在classes.dex和.odex文件,造成空间浪费。
ODEX优化提高首次开机速度
现在Android手机都需要预装很多应用,这些应用的APK主要在/system/app,/system/priv-app/,/system/vendor/app等目录下。如果没有做ODEX优化,在首次开机时,SystemService.java会调用PackageManagerService对这几个目录下的APK做dexopt的优化,生成oat文件。APK越多,首次开机的事件就会越长。首次开机时,通常在手机上看到“正在优化第*个应用,总共*个应用”,这就是在对APK做dexopt的优化。
如果我们要提高首次开机的速度,可以做如下设置:
1、 在工程代码/device/qcom/项目名/BoardConfig.mk修改两个设置
修改下面两个设置,在编译时,对jar、APK都做ODEX优化,生成对应的ODEX文件:
DISABLE_DEXPREOPT := false
WITH_DEXPREOPT := true
如果不想在编译时做ODEX优化,可以注释掉这两行,或者把这两个值设置成:
注释:
#DISABLE_DEXPREOPT := false
#WITH_DEXPREOPT := true
或:
DISABLE_DEXPREOPT := true
WITH_DEXPREOPT := false
2、 如果设置了:
DISABLE_DEXPREOPT := false
WITH_DEXPREOPT := true
在编译的时候,/system/framework/目录下面的jar包,和/system/app,/system/priv-app/,/system/vendor/app等目录下的apk文件,都会在编译时做ODEX优化。
如果不想jar包做ODEX优化,可以在/build/core/java_library.mk文件中设置:
LOCAL_DEX_PREOPT := false
这样在编译时,jar包就不会做ODEX优化。
3、 在实际开发中,有些apk如果做了ODEX优化,可能会出问题,可以通过在apk的编译目录Android.mk文件中添加:
LOCAL_DEX_PREOPT := false
这样该apk就不会做ODEX优化。
如果jar包和apk都做ODEX优化,在项目中,原来首次开机速度3分钟左右可以提高到1分种左右。
源码解惑
PackageManagerService初始化
在PackageManagerService初始化过程中,安装应用的时,PKMS都会通过另一个类Installer的方法dexopt()来对APK里面的dex字节码进行优化:
try {
...........
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
getCompilerFilterForReason(REASON_SHARED_APK),
StorageManager.UUID_PRIVATE_INTERNAL,
SKIP_SHARED_LIBRARY_CHECK);
}
} catch (FileNotFoundException e) {
.......
} catch (IOException | InstallerException e) {
.......
}
在Installer类中调用到InstallerConnection中的dexopt()方法:
public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter,
String volumeUuid, String sharedLibraries) throws InstallerException {
execute("dexopt",
apkPath,
uid,
pkgName,
instructionSet,
dexoptNeeded,
outputPath,
dexFlags,
compilerFilter,
volumeUuid,
sharedLibraries);
}
InstallerConnection通过socket向守护进程installd发送一个dexopt请求,这个请求是由installd里面的函数do_dexopt来处理的:
static int do_dexopt(char **arg, char reply[REPLY_MAX])
{
const char* args[DEXOPT_PARAM_COUNT];
for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
CHECK(arg[i] != nullptr);
args[i] = arg[i];
}
int dexopt_flags = atoi(arg[6]);
DexoptFn dexopt_fn;
if ((dexopt_flags & DEXOPT_OTA) != 0) {//系统升级
dexopt_fn = do_ota_dexopt;
} else {
dexopt_fn = do_regular_dexopt;
}
return dexopt_fn(args, reply);
}
static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
return dexopt(args);
}
dexopt函数位于同目录下的commands.cpp文件中:
int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries)
{
//Application should be visible to everyone
bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
//DEXOPT_SAFEMODE:Application wants to run in VM safe mode
bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
//DEXOPT_BOOTCOMPLETE:The system boot has finished
bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
//Hint that the dexopt type is profile-guided.
bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
// Don't use profile for vm_safe_mode. b/30688277
profile_guided = profile_guided && !vm_safe_mode;
CHECK(pkgname != nullptr);
CHECK(pkgname[0] != 0);
// Public apps should not be compiled with profile information ever. Same goes for the special
// package '*' used for the system server.
Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
if (!is_public && pkgname[0] != '*') {
// Open reference profile in read only mode as dex2oat does not get write permissions.
const std::string pkgname_str(pkgname);
reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
[pkgname_str]() {
clear_reference_profile(pkgname_str.c_str());
});
// Note: it's OK to not find a profile here.
}
if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
LOG_FATAL("dexopt flags contains unknown fields\n");
}
char out_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {//创建oat输出路径
return false;
}
const char *input_file;
char in_odex_path[PKG_PATH_MAX];
switch (dexopt_needed) {
case DEXOPT_DEX2OAT_NEEDED:
input_file = apk_path;
break;
case DEXOPT_PATCHOAT_NEEDED:
if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {//odex文件路径
return -1;
}
input_file = in_odex_path;
break;
case DEXOPT_SELF_PATCHOAT_NEEDED:
input_file = out_path;
break;
default:
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
return 72;
}
struct stat input_stat;
memset(&input_stat, 0, sizeof(input_stat));
stat(input_file, &input_stat);
base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
if (input_fd.get() < 0) {
ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
return -1;
}
const std::string out_path_str(out_path);
Dex2oatFileWrapper<std::function<void ()>> out_fd(
open_output_file(out_path, /*recreate*/true, /*permissions*/0644),
[out_path_str]() { unlink(out_path_str.c_str()); });
if (out_fd.get() < 0) {
ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
return -1;
}
if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) {
return -1;
}
// Create a swap file if necessary.
base::unique_fd swap_fd;
if (ShouldUseSwapFileForDexopt()) {
// Make sure there really is enough space.
char swap_file_name[PKG_PATH_MAX];
strcpy(swap_file_name, out_path);
if (add_extension_to_file_name(swap_file_name, ".swap")) {
swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
}
if (swap_fd.get() < 0) {
// Could not create swap file. Optimistically go on and hope that we can compile
// without it.
ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
} else {
// Immediately unlink. We don't really want to hit flash.
if (unlink(swap_file_name) < 0) {
PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
}
}
}
// Avoid generating an app image for extract only since it will not contain any classes.
Dex2oatFileWrapper<std::function<void ()>> image_fd;
const std::string image_path = create_image_filename(out_path);
if (!image_path.empty()) {
char app_image_format[kPropertyValueMax];
bool have_app_image_format =
get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
// Use app images only if it is enabled (by a set image format) and we are compiling
// profile-guided (so the app image doesn't conservatively contain all classes).
if (profile_guided && have_app_image_format) {
// Recreate is true since we do not want to modify a mapped image. If the app is
// already running and we modify the image file, it can cause crashes (b/27493510).
image_fd.reset(open_output_file(image_path.c_str(),
true /*recreate*/,
0600 /*permissions*/),
[image_path]() { unlink(image_path.c_str()); }
);
if (image_fd.get() < 0) {
// Could not create application image file. Go on since we can compile without
// it.
LOG(ERROR) << "installd could not create '"
<< image_path
<< "' for image file during dexopt";
} else if (!set_permissions_and_ownership(image_fd.get(),
is_public,
uid,
image_path.c_str())) {
image_fd.reset(-1);
}
}
// If we have a valid image file path but no image fd, explicitly erase the image file.
if (image_fd.get() < 0) {
if (unlink(image_path.c_str()) < 0) {
if (errno != ENOENT) {
PLOG(ERROR) << "Couldn't unlink image file " << image_path;
}
}
}
}
ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
pid_t pid = fork();
if (pid == 0) {//子进程
/* child -- drop privileges before continuing */
drop_capabilities(uid);//调整优先级
SetDex2OatAndPatchOatScheduling(boot_complete);
if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
_exit(67);
}
if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
|| dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
run_patchoat(input_fd.get(),
out_fd.get(),
input_file,
out_path,
pkgname,
instruction_set);
} else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
// Pass dex2oat the relative path to the input file.
const char *input_file_name = get_location_from_path(input_file);
run_dex2oat(input_fd.get(),
out_fd.get(),
image_fd.get(),
input_file_name,
out_path,
swap_fd.get(),
instruction_set,
compiler_filter,
vm_safe_mode,
debuggable,
boot_complete,
reference_profile_fd.get(),
shared_libraries);
} else {
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
_exit(73);
}
_exit(68); /* only get here on exec failure */
} else {
int res = wait_child(pid);
if (res == 0) {
ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
} else {
ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
return -1;
}
}
struct utimbuf ut;
ut.actime = input_stat.st_atime;
ut.modtime = input_stat.st_mtime;
utime(out_path, &ut);
// We've been successful, don't delete output.
out_fd.SetCleanup(false);
image_fd.SetCleanup(false);
reference_profile_fd.SetCleanup(false);
return 0;
}
}
构造路径,打开原文件,打开(没有就创建)odex文件,然后fork一个进程执行优化,主要的优化在run_dex2oat()函数中:
static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_file_name,
const char* output_file_name, int swap_fd, const char *instruction_set,
const char* compiler_filter, bool vm_safe_mode, bool debuggable, bool post_bootcomplete,
int profile_fd, const char* shared_libraries) {
static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
ALOGE("Instruction set %s longer than max length of %d",
instruction_set, MAX_INSTRUCTION_SET_LEN);
return;
}
char dex2oat_Xms_flag[kPropertyValueMax];
bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;//获取属性值dalvik.vm.dex2oat-Xms
char dex2oat_Xmx_flag[kPropertyValueMax];
bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
char dex2oat_threads_buf[kPropertyValueMax];
bool have_dex2oat_threads_flag = get_property(post_bootcomplete
? "dalvik.vm.dex2oat-threads"
: "dalvik.vm.boot-dex2oat-threads",
dex2oat_threads_buf,
NULL) > 0;
char dex2oat_threads_arg[kPropertyValueMax + 2];
if (have_dex2oat_threads_flag) {
sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
}
char dex2oat_isa_features_key[kPropertyKeyMax];
sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
char dex2oat_isa_features[kPropertyValueMax];
bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
dex2oat_isa_features, NULL) > 0;
char dex2oat_isa_variant_key[kPropertyKeyMax];
sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
char dex2oat_isa_variant[kPropertyValueMax];
bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
dex2oat_isa_variant, NULL) > 0;
const char *dex2oat_norelocation = "-Xnorelocate";
bool have_dex2oat_relocation_skip_flag = false;
char dex2oat_flags[kPropertyValueMax];
int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
// If we booting without the real /data, don't spend time compiling.
char vold_decrypt[kPropertyValueMax];
bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
bool skip_compilation = (have_vold_decrypt &&
(strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
(strcmp(vold_decrypt, "1") == 0)));
bool generate_debug_info = property_get_bool("debug.generate-debug-info");
char app_image_format[kPropertyValueMax];
char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
bool have_app_image_format =
image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
if (have_app_image_format) {
sprintf(image_format_arg, "--image-format=%s", app_image_format);
}
char dex2oat_large_app_threshold[kPropertyValueMax];
bool have_dex2oat_large_app_threshold =
get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
if (have_dex2oat_large_app_threshold) {
sprintf(dex2oat_large_app_threshold_arg,
"--very-large-app-threshold=%s",
dex2oat_large_app_threshold);
}
static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
static const char* RUNTIME_ARG = "--runtime-arg";
static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX];
char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
char instruction_set_variant_arg[strlen("--instruction-set-variant=") + kPropertyValueMax];
char instruction_set_features_arg[strlen("--instruction-set-features=") + kPropertyValueMax];
char dex2oat_Xms_arg[strlen("-Xms") + kPropertyValueMax];
char dex2oat_Xmx_arg[strlen("-Xmx") + kPropertyValueMax];
char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
bool have_dex2oat_swap_fd = false;
char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN];
bool have_dex2oat_image_fd = false;
char dex2oat_image_fd[strlen("--app-image-fd=") + MAX_INT_LEN];
sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
if (swap_fd >= 0) {
have_dex2oat_swap_fd = true;
sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
}
if (image_fd >= 0) {
have_dex2oat_image_fd = true;
sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
}
if (have_dex2oat_Xms_flag) {
sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
}
if (have_dex2oat_Xmx_flag) {
sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
}
// Compute compiler filter.
bool have_dex2oat_compiler_filter_flag;
if (skip_compilation) {
strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
have_dex2oat_compiler_filter_flag = true;
have_dex2oat_relocation_skip_flag = true;
} else if (vm_safe_mode) {
strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
have_dex2oat_compiler_filter_flag = true;
} else if (compiler_filter != nullptr &&
strlen(compiler_filter) + strlen("--compiler-filter=") <
arraysize(dex2oat_compiler_filter_arg)) {
sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
have_dex2oat_compiler_filter_flag = true;
} else {
char dex2oat_compiler_filter_flag[kPropertyValueMax];
have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
dex2oat_compiler_filter_flag, NULL) > 0;
if (have_dex2oat_compiler_filter_flag) {
sprintf(dex2oat_compiler_filter_arg,
"--compiler-filter=%s",
dex2oat_compiler_filter_flag);
}
}
// Check whether all apps should be compiled debuggable.
if (!debuggable) {
char prop_buf[kPropertyValueMax];
debuggable =
(get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
(prop_buf[0] == '1');
}
char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
if (profile_fd != -1) {
sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
}
ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
const char* argv[7 // program name, mandatory arguments and the final NULL
+ (have_dex2oat_isa_variant ? 1 : 0)
+ (have_dex2oat_isa_features ? 1 : 0)
+ (have_dex2oat_Xms_flag ? 2 : 0)
+ (have_dex2oat_Xmx_flag ? 2 : 0)
+ (have_dex2oat_compiler_filter_flag ? 1 : 0)
+ (have_dex2oat_threads_flag ? 1 : 0)
+ (have_dex2oat_swap_fd ? 1 : 0)
+ (have_dex2oat_image_fd ? 1 : 0)
+ (have_dex2oat_relocation_skip_flag ? 2 : 0)
+ (generate_debug_info ? 1 : 0)
+ (debuggable ? 1 : 0)
+ (have_app_image_format ? 1 : 0)
+ dex2oat_flags_count
+ (profile_fd == -1 ? 0 : 1)
+ (shared_libraries != nullptr ? 4 : 0)
+ (have_dex2oat_large_app_threshold ? 1 : 0)];
int i = 0;
argv[i++] = DEX2OAT_BIN;
argv[i++] = zip_fd_arg;
argv[i++] = zip_location_arg;
argv[i++] = oat_fd_arg;
argv[i++] = oat_location_arg;
argv[i++] = instruction_set_arg;
if (have_dex2oat_isa_variant) {
argv[i++] = instruction_set_variant_arg;
}
if (have_dex2oat_isa_features) {
argv[i++] = instruction_set_features_arg;
}
if (have_dex2oat_Xms_flag) {
argv[i++] = RUNTIME_ARG;
argv[i++] = dex2oat_Xms_arg;
}
if (have_dex2oat_Xmx_flag) {
argv[i++] = RUNTIME_ARG;
argv[i++] = dex2oat_Xmx_arg;
}
if (have_dex2oat_compiler_filter_flag) {
argv[i++] = dex2oat_compiler_filter_arg;
}
if (have_dex2oat_threads_flag) {
argv[i++] = dex2oat_threads_arg;
}
if (have_dex2oat_swap_fd) {
argv[i++] = dex2oat_swap_fd;
}
if (have_dex2oat_image_fd) {
argv[i++] = dex2oat_image_fd;
}
if (generate_debug_info) {
argv[i++] = "--generate-debug-info";
}
if (debuggable) {
argv[i++] = "--debuggable";
}
if (have_app_image_format) {
argv[i++] = image_format_arg;
}
if (have_dex2oat_large_app_threshold) {
argv[i++] = dex2oat_large_app_threshold_arg;
}
if (dex2oat_flags_count) {
i += split(dex2oat_flags, argv + i);
}
if (have_dex2oat_relocation_skip_flag) {
argv[i++] = RUNTIME_ARG;
argv[i++] = dex2oat_norelocation;
}
if (profile_fd != -1) {
argv[i++] = profile_arg;
}
if (shared_libraries != nullptr) {
argv[i++] = RUNTIME_ARG;
argv[i++] = "-classpath";
argv[i++] = RUNTIME_ARG;
argv[i++] = shared_libraries;
}
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
execv(DEX2OAT_BIN, (char * const *)argv);//execv执行/system/bin/dex2oat
ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
}
run_dex2oat通过/system/bin/dex2oat来将dex文件翻译成oat文件,实际上就是将dex字节码翻译成本地机器码,并且保存在一个oat文件中。
我们暂时分析到这里,/system/bin/dex2oat的具体操作可查看源码,参考:http://blog.csdn.net/hl09083253cy/article/details/78418809
最后,还有一个地方需要注意的是,应用程序的安装发生在两个时机,第一个时机是系统启动的时候(上面的分析),第二个时机系统启动完成后用户自行安装的时候。在第一个时机中,系统除了会对/system/app和/data/app目录下的所有APK进行dex字节码到本地机器码的翻译之外,还会对/system/framework目录下的APK或者JAR文件,以及这些APK所引用的外部JAR,进行dex字节码到本地机器码的翻译。这样就可以保证除了应用之外,系统中使用Java来开发的系统服务,也会统一地从dex字节码翻译成本地机器码。也就是说,将Android系统中的Dalvik虚拟机替换成ART运行时之后,系统中的代码都是由ART运行时来执行的了,这时候就不会对Dalvik虚拟机产生任何的依赖。