深入理解Android Framework(六)- Framework重要服务之PackageManagerService(三) dex优化

771 阅读2分钟

上一章分析了PMS的APP安装流程,APK经过复制、创建对应包文件夹、安装之后,还剩一个比较重要的点需要分析,那就是dex编译,本章将结合Android 11继续探究dex的编译流程。

在executePostCommitSteps中,存在如下代码:

private void executePostCommitSteps(CommitRequest commitRequest) {
        // 判断是否需要dex优化
    final boolean performDexopt =(!instantApp || Global.getInt(mContext.getContentResolver(),
    Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
    && !pkg.isDebuggable()
    && (!onIncremental)
    && dexoptOptions.isCompilationEnabled();
    // 如有必要,执行dex优化
    if (performDexopt){
    // Compile the layout resources.
    if(SystemProperties.getBoolean(PRECOMPILE_LAYOUTS,false)){
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,"compileLayouts");
        mViewCompiler.compileLayouts(pkg);
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,"dexopt");
    ScanResult result=reconciledPkg.scanResult;
    PackageSetting realPkgSetting=result.existingSettingCopied?result.request.pkgSetting:result.pkgSetting;
    if(realPkgSetting==null){
        realPkgSetting=reconciledPkg.pkgSetting;
    }

    // Unfortunately, the updated system app flag is only tracked on this PackageSetting
    boolean isUpdatedSystemApp=reconciledPkg.pkgSetting.getPkgState().isUpdatedSystemApp();
    realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
    ...
    }
}

在这其中存在dex优化方法performDexOpt(),下面继续深入分析该方法。

1、performDexOpt

frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java
int performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
    String[] instructionSets, CompilerStats.PackageStats packageStats,
    PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
        ...
        // 开始执行dex优化

    synchronized (mInstallLock) {
        final long acquireTime = acquireWakeLockLI(pkg.getUid());
        try {
            return performDexOptLI(pkg, pkgSetting, instructionSets,packageStats, packageUseInfo, options);
        } finally {
         releaseWakeLockLI(acquireTime);

        }
    }
    ...
}

可以看到,该方法调用了performDexOptLI()方法

2、performDexOptLI

frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java
private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
        String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
        PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
        ...
        for (String dexCodeIsa : dexCodeInstructionSets) {
            // 执行dexOptPath
            int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,packageStats, options.isDowngrade(), profileName, dexMetadataPath,
            options.getCompilationReason());
            ...
            if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
                result = newResult;
            }
        }
        ...
 }

3、 dexOptPath

 frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@GuardedBy("mInstallLock")
private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path,
        String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext,
        int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
        String profileName, String dexMetadataPath, int compilationReason) {
            ...
            mInstaller.dexopt(path, uid, pkg.getPackageName(), isa, dexoptNeeded, oatDir,
            dexoptFlags, compilerFilter, pkg.getVolumeUuid(), classLoaderContext,
            seInfo, false /* downgrade*/, pkg.getTargetSdkVersion(),
            profileName, dexMetadataPath,
            getAugmentedReasonName(compilationReason, dexMetadataPath != null));
            ...
        }
        // [frameworks/base/services/core/java/com/android/server/pm/Installer.java]
        // dexopt优化方法, 该方法通过binder与 frameworks/native/cmds/installd/installd.cpp 进行通信
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
        int dexoptNeeded, @Nullable String outputPath, int dexFlags,
        String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
        @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
        @Nullable String profileName, @Nullable String dexMetadataPath,
        @Nullable String compilationReason) throws InstallerException {
            assertValidInstructionSet(instructionSet);
            BlockGuard.getVmPolicy().onPathAccess(apkPath);
            BlockGuard.getVmPolicy().onPathAccess(outputPath);
            BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
            if (!checkBeforeRemote()) return;
            try {
                mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
                dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
                targetSdkVersion, profileName, dexMetadataPath, compilationReason);
            } catch (Exception e) {
             throw InstallerException.from(e);
            }
}

在这里可以看出,dexopt方法通过binder与frameworks/native/cmds/installd/installd.cpp进行通信,调用了installd.cpp的dexopt方法。

4、dexopt

frameworks/native/cmds/installd/dexopt.cpp
int dexopt(const char* dex_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, const char* class_loader_context, const char* se_info,
bool downgrade, int target_sdk_version, const char* profile_name,
const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg,
/* out */ bool* completed) {
...
// 使用dex2opt进行dex优化
RunDex2Oat runner(dex2oat_bin, execv_helper.get());
runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(),
in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
debuggable, boot_complete, for_restore, target_sdk_version,
enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
compilation_reason);
...
}

这个方法最终调用了dex2opt对dex文件进行优化,值得注意的时,dex2opttargetSdkVersion>=29且**Android 10+**的系统上不再允许在应用进程上触发dex2oat编译。其原因是系统在targetSdkVersion=29的时候,对此做了限制,不允许应用进程上触发dex2oat编译,通过应用进程触发dex2oat编译来弥补系统触发dex2oat编译的不足的方式,已经在andorid 10+的系统上不再适用,这点需要特别注意。