Android Q ART-Google Play安装时优化原理

Google Play现在除了APK文件之外,还会交付一套基于云端的ART Profile配置文件Dex Metadata(.dm)。

这个dm文件是从大数据用户那里搜集整理的APK对应的"热代码"文件,在通过GP安装apk时,会跟据dm文件提前进行优化编译,而不必等到用户使用一段时间生成热代码后再编译,可以显著提升首次启动速度。

Android P或更高版本系统的设备目前已提供相关支持。

1. 查看.prof、.dm文件
dex metadata(.dm)内容跟.prof一致,其实是一个jar(zip)压缩包,里面包含一个primary.prof。
安装apk时传.prof不识别,只识别.dm格式。

  1. 手动编译和清除一个APK:profile模式
    cmd package compile -m speed-profile -f com.whatsapp
    cmd package compile --reset com.whatsapp

  2. 手动编译和清除所有APK:profile模式
    cmd package compile -m speed-profile -f -a
    cmd package compile --reset -a

  3. 查看profile、dex metadata文件
    profman --profile-file=xxx.prof --dump-only
    profman --profile-file=xxx.dm --dump-only

    === profile ===
    ProfileInfo:
    base.apk [index=0]
            hot methods:
            startup methods: 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
            post startup methods: 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
            classes: 13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
    

2. 对比使用.dm安装前后差异
以whatsapp为例,我们先运行一会儿whatsapp生成primary.prof文件,然后压缩成zip改名为WhatsApp.dm,我们就自己制作了dex metdata。

如下实验可以看到加上.dm后生成的.art、.odex文件明显变大了,安装时就进行了oat编译提高启动速度。
before: 279K 2019-10-31 10:18 base.odex
after: 1.7M 2019-10-31 10:14 base.odex

默认安装
pm install WhatsApp.apk

/data/app/com.whatsapp/oat/x86
-rw-r--r-- 1 system all_a71 40K 2019-10-31 10:18 base.art
-rw-r--r-- 1 system all_a71 279K 2019-10-31 10:18 base.odex
-rw-r--r-- 1 system all_a71 11M 2019-10-31 10:18 base.vdex

/data/misc/profiles/cur/0/com.whatsapp/primary.prof 大小为0
/data/misc/profiles/ref/com.whatsapp 目录为空

带有.dm安装
pm install-create : created install session [1918895757]
pm install-write 1918895757 WhatsApp.apk /data/local/tmp/WhatsApp.apk
pm install-write 1918895757 WhatsApp.dm /data/local/tmp/WhatsApp.dm //传入dm参数
pm install-commit 1918895757

/data/app/com.whatsapp
base.apk base.dm lib oat

/data/app/com.whatsapp/oat/x86
-rw-r--r-- 1 system all_a70 304K 2019-10-31 10:14 base.art
-rw-r--r-- 1 system all_a70 1.7M 2019-10-31 10:14 base.odex
-rw-r--r-- 1 system all_a70 11M 2019-10-31 10:14 base.vdex

/data/misc/profiles/cur/0/com.whatsapp/primary.prof 大小为0
/data/misc/profiles/ref/com.whatsapp/primary.prof 内容与.dm一致

3. 代码中如何使用profile文件

  • 3.1 prepareAppProfiles()生成primary.prof

    1. PMS:installPackageLI():
    
    mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
    
    
    2. ArtManagerService:prepareAppProfiles():
    
    String codePath = codePathsProfileNames.keyAt(i);///data/app/com.whatsapp-SA9Yu-bYC8NLh1_b2iKULg==/base.apk
    String profileName = codePathsProfileNames.valueAt(i);//primary.prof
    File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));//null or base.dm
    String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();//null or base.dm
    synchronized (mInstaller) {
        boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
                profileName, codePath, dexMetadataPath);
    }
    
    3. dexopt.cpp:prepare_app_profile():
    //code_path= /data/app/com.whatsapp-l1rTMW3FR7a81Q7SkZoAZQ==/base.apk
    //cur_profile= /data/misc/profiles/cur/0/com.whatsapp/primary.prof
    //ref_profile= /data/misc/profiles/ref/com.whatsapp/primary.prof
    
    //1.创建primary.prof文件
    std::string cur_profile  = create_current_profile_path(user_id, package_name, profile_name,
            /*is_secondary_dex*/ false);
    if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) {
        return false;
    }
    
    // 2. 没有dex metadata不再往下走
    // Check if we need to install the profile from the dex metadata.
    if (dex_metadata == nullptr) {
        return true;
    }
    
    // 3. 将dex metadata和cur primary.prof合并到ref primary.prof
    // 第一次安装apk时,dex metadata==ref profile
    // 启动whatsapp自动生成热文件,执行cmd package compile -m speed-profile后,cur下primary.prof变为0,转移到了ref
    
    unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name,
            /*read_write*/ true, /*is_secondary_dex*/ false);
    unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY(
            open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW)));
    unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW)));
    
    pid_t pid = fork();
    if (pid == 0) {
        /* child -- drop privileges before continuing */
        gid_t app_shared_gid = multiuser_get_shared_gid(user_id, app_id);
        drop_capabilities(app_shared_gid);
    
        // The copy and update takes ownership over the fds.
        run_profman_copy_and_update(std::move(dex_metadata_fd),
                                    std::move(ref_profile_fd),
                                    std::move(apk_fd),
                                    code_path);
    }
    
    
  • 3.2 dex2oat调用.prof编译
    cur_profile和ref_profile只能传入一个,两个都传会报错。
    第一次安装apk时,如果有dex metadata文件,那么ref_profile不为空,dex2oat就会以ref_profile为热代码文件进行编译。

    1. PackageDexOptimizer.java:dexOptPath():
    mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
            compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
            false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
            profileName, dexMetadataPath, getReasonName(compilationReason));
    
    2. installd/dexopt.cpp:dexopt():
    // 打开ref profile
    Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
            pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
    
    //打开dex_metadata文件
    unique_fd dex_metadata_fd;
    if (dex_metadata_path != nullptr) {
        dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
        if (dex_metadata_fd.get() < 0) {
            PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path;
        }
    }
    //执行dex2oat编译
    run_dex2oat(input_fd.get(),
                out_oat_fd.get(),
                in_vdex_fd.get(),
                out_vdex_fd.get(),
                image_fd.get(),
                dex_path,
                out_oat_path,
                swap_fd.get(),
                instruction_set,
                compiler_filter,
                debuggable,
                boot_complete,
                background_job_compile,
                reference_profile_fd.get(),
                class_loader_context,
                target_sdk_version,
                enable_hidden_api_checks,
                generate_compact_dex,
                dex_metadata_fd.get(),
                compilation_reason);
    
    
    3. dex2oat/dex2oat.cc:Dex2oat():
    //加载profile文件
    if (dex2oat->UseProfile()) {
        if (!dex2oat->LoadProfile()) {
        return dex2oat::ReturnCode::kOther;
        }
    }
    
    4. dex2oat.cc:LoadProfile():
    //profile_file_fd_指ref profile,第一次安装dex metadata==ref profile
    if (profile_file_fd_ != -1) {
        profile_file = LockedFile::DupOf(profile_file_fd_, "profile",
                                        true /* read_only_mode */, &error);
    } else if (profile_file_ != "") {
        profile_file = LockedFile::Open(profile_file_.c_str(), O_RDONLY, true, &error);
    }
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容