Android应用管理四 -- APK包的安装、卸载和优化(PackageManagerService)

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虚拟机产生任何的依赖。

    原文作者:Kitty_London
    原文地址: https://blog.csdn.net/Kitty_Landon/article/details/79106443
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞