Android 编译优化——dex2oat编译

这篇具有很好参考价值的文章主要介绍了Android 编译优化——dex2oat编译。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、ART 即时 (JIT) 编译器实现

Android Runtime (ART) 包含一个具备代码分析功能的即时 (JIT) 编译器,该编译器可以在 Android 应用运行时持续提高其性能。JIT 编译器对 Android 运行组件当前的预先 (AOT) 编译器进行了补充,可以提升运行时性能,节省存储空间,加快应用和系统更新速度。相较于 AOT 编译器,JIT 编译器的优势也更为明显,因为在应用自动更新期间或在无线下载 (OTA) 更新期间重新编译应用时,它不会拖慢系统速度。

尽管 JIT 和 AOT 使用相同的编译器,它们所进行的一系列优化也较为相似,但它们生成的代码可能会有所不同。JIT 会利用运行时类型信息,可以更高效地进行内联,并可让堆栈替换 (OSR) 编译成为可能,而这一切都会使其生成的代码略有不同。

  • 1、OAT文件可以直接运行
  • 2、非OAT文件需要解释运行
  • 3、如果是代码达到热点的方法的阈值,会让JIT在后台编译,编译之后添加到缓存中
  • 4、JIT Code Cache 中的代码等到下次运行时如果命中缓存,直接使用缓存代码执行(Code Cache是内存缓存)

JIT 编译涉及以下活动

  1. 用户运行应用,此举随后触发 ART 加载 .dex 文件。
    • 如果有 .oat 文件(即 .dex 文件的 AOT 二进制文件),ART 会直接使用该文件。虽然 .oat 文件会定期生成,但文件中不一定会包含经过编译的代码(即 AOT 二进制文件)。
    • 如果 .oat 文件不含经过编译的代码,ART 会通过 JIT 和解释器执行 .dex 文件。
  2. 针对任何未根据 speed 编译过滤器编译的应用启用 JIT(也就是说,要尽可能多地编译应用中的代码)。
  3. 将 JIT 配置文件数据转储到只有该应用可以访问的系统目录下的文件中。
  4. AOT 编译 ( dex2oat ) 守护程序通过解析该文件来推进其编译。

JIT 工作流程

(注意:JIT解释器解释完其实可以立即执行,改图未标注)

  • 分析信息会存储在代码缓存中,并会在内存紧张时作为垃圾被回收。
    • 无法保证在应用处于后台运行状态时所捕获的快照能够包含完整的数据(即 JIT 编译的所有内容)。
    • 该过程不会尝试确保记录所有内容(因为这会影响运行时性能)。
  • 方法可能有三种不同的状态:
    • 已经过解释(dex 代码)
    • 已经过 JIT 编译
    • 已经过 AOT 编译
    如果同时存在 JIT 和 AOT 代码(例如,由于反复进行逆优化),经过 JIT 编译的代码将是首选代码。
  • 在不影响前台应用性能的情况下运行 JIT 所需的内存取决于相关应用。大型应用比小型应用需要更多内存。一般来说,大型应用所需的内存稳定维持在 4 MB 左右。
  • Carbage Collect清理内存,为JIT Code Cache腾出空间

二、dex2oat编译

compiler_filter.h ,我们可以看到dex2oat一共有12种编译模式:

enum Filter {   
    VerifyNone,           // Skip verification but mark all classes as verified anyway.
    kVerifyAtRuntime,     // Delay verication to runtime, do not compile anything.
    kVerifyProfile,       // Verify only the classes in the profile, compile only JNI stubs.
    kInterpretOnly,       // Verify everything, compile only JNI stubs.
    kTime,                // Compile methods, but minimize compilation time.
    kSpaceProfile,        // Maximize space savings based on profile.
    kSpace,               // Maximize space savings.
    kBalanced,            // Good performance return on compilation investment.
    kSpeedProfile,        // Maximize runtime performance based on profile.
    kSpeed,               // Maximize runtime performance.
    kEverythingProfile,   // Compile everything capable of being compiled based on profile.
    kEverything,          // Compile everything capable of being compiled.
};

注意:android官方上出现了quicken模式解释为:“运行 DEX 代码验证,并优化一些 DEX 指令,以获得更好的解译器性能”,因此可以推断出interpret-only和quicken相似

以上12种编译模式 按照排列次序逐渐增强 ,那系统默认采用了哪些编译模式呢?我们可以在在手机上执行 getprop | grep pm 查看:

pm.dexopt.ab-ota: [speed-profile]
pm.dexopt.bg-dexopt: [speed-profile]
pm.dexopt.boot: [verify-profile]
pm.dexopt.core-app: [speed]
pm.dexopt.first-boot: [interpret-only]
pm.dexopt.forced-dexopt: [speed]
pm.dexopt.install: [interpret-only]
pm.dexopt.nsys-library: [speed]
pm.dexopt.shared-apk: [speed]
[dalvik.vm.heapmaxfree]: [8m]
[dalvik.vm.heapminfree]: [512k]
[persist.radio.apm_sim_not_pwdn]: [1]
[pm.dexopt.ab-ota]: [speed-profile]
[pm.dexopt.bg-dexopt]: [speed-profile]
[pm.dexopt.boot]: [verify]
[pm.dexopt.first-boot]: [quicken]
[pm.dexopt.inactive]: [verify]
[pm.dexopt.install]: [speed-profile]
[pm.dexopt.shared]: [speed]

其中有几个我们是特别关心的,

  1. install (应用安装)与 first-boot (应用首次启动)使用的是[interpret-only],即只verify,代码解释执行即不编译任何的机器码,它的性能与Dalvik时完全一致,先让用户愉快的玩耍起来。
  2. ab-ota (系统升级)与 bg-dexopt (后台编译)使用的是[speed-profile],即只根据“热代码”的profile配置来编译。这也是N中混合编译的核心模式。
  3. 对于动态加载的代码,即 forced-dexopt ,它采用的是[speed]模式,即最大限度的编译机器码,它的表现与之前的AOT编译一致。

总的来说,程序使用loaddex动态加载的代码是无法享受混合编译带来的好处,我们应当尽量 采用ClassN.dex方式来符合Google的规范 。这不仅在ota还是混合编译上,都会带来很大的提升。

dex2oat支持的编译模式

dex2oat编译,android

注意:Android没有明确对balanced支持,另外,android默认支持的编译模式是quicken。

N版本中dex2oat的原理及模式

N版本当中强化了JIT模式。JIT模式是Just in time 的简称。意思是在运行的时候,根据method有使用频率来决定是否要对某一个方法进行优化。虚拟机会统计每一个方法被执行的次数。如果某一个方法执行的次数比较多,达到一定的阈值,就会将升级为hot method,并将其记录在一个profile当中。在系统空闲并且在充电的时候,只将这些方法进行优化。在运行的时候,也会对这些方法进行优化,以方便在运行的时候使用。

dex2oat编译,android

dex2oat编译,android

dex2oat的命令行参数

首先我们先看一下dex2oat都支持一些什么样的命令行参数:

通用类

  • -j<线程数>:编译时使用多少个线程。缺省值为默认的CPU核数。例:-j8

输入输出的文件类

  • --dex-file=:待编译的.dex, .jar或者.apk文件
  • --dex-location=:dex文件的路径
  • --zip-fd=:包含classes.dex文件的zip的文件描述符
  • --zip-location=:zip文件路径
  • --oat-file=<输出的oat文件名>:输出的oat文件名
  • --oat-fd=<输出文件描述符>:输出文件描述符
  • --oat-location=<输出的oat文件的路径>:输出的oat文件的路径
  • --oat-symbols=:指定输出完整符号的oat路径
  • --image=:指定输出的image文件名
  • --image-classes=<预编译文件列表>:指定preloaded-classes的路径,例:--image=frameworks/base/preloaded-classes
  • --base=:指定boot image的基地址,例:--base=0x50000000
  • --boot-image=:指定boot class的文件。例:--boot-image=/system/framework/boot.art,默认值为:$ANDROID_ROOT/system/framework/boot.art
  • --android-root=<路径>:portable linking所用的库的路径。例:--android-root=out/host/linux-x86,默认值:$ANDROID_ROOT

指令集类

  • --instruction-set=(arm|arm64|mips|mips64|x86|x86_64):指定编译的指令集。例:--instruction-set=x86,默认:arm
  • --instruction-set-features=<指令集参数>。例:--instruction-set-features=div,默认:default

编译器优化选项类

  • --compile-pic:Force indirect use of code, methods, and classes. 默认:disabled
  • --compiler-filter=(verify-none| interpret-only| space |balanced |speed |everything |time):选择compiler filter。例:--compiler-filter=everything。默认值:speed
  • --huge-method-max=<方法指令数>:巨型方法的指令数,用于编译器调优。例:--huge-method-max=10000,默认值:10000
  • --large-method-max=<方法指令数>:大型方法的指令数,用于编译器调优。例:--large-method-max=600,默认值:600
  • --small-method-max=<方法指令数>:小型方法的指令数,用于编译器调优。例:--small-method-max=60,默认值:60
  • --tiny-method-max=<方法指令数>:微型方法的指令数,用于编译器调优。例:--tiny-method-max=20,默认值:20
  • --num-dex-methods=<方法数>:小型dex文件的方法上限,用于编译器调优。如果发现是个小型的dex文件,而编译器的filter不是interpret-only或者verify-none的话,就用speed filter。例:--num-dex-method=900,默认值:900
  • --inline-depth-limit=<深度限制>:编译器调优用,只建议开发和实验用。例:--inline-depth-limit=5,默认值:5
  • --inline-max-code-units=<方法数>:inline调优用,实验用。例:--inline-max-code-units=100,默认值:100
  • --dump-timing: 显示时间都花到哪儿去了。

重定位信息类

  • --include-patch-information:编译时包含patch信息,可以在不重编的情况下重定位。
  • --no-include-patch-information:不包含patch信息。

调试信息类

  • -g:与--generate-debug-info相同
  • --generate-debug-info:生成所有可用的调试信息。可以通过标准的strip命令或者objcopy命令来压缩掉无用信息。
  • --no-generate-debug-info:不生成调试信息

运行参数类

  • --runtime-arg <参数>:指定运行时参数,如:初始堆大小,最大堆大小,详细输出等。每次只能传一个参数。例:--runtime-arg -Xms256m
  • --profile-file=:指定profile信息,供编译器例用

编译选项类

  • --print-pass-names: 打印pass name信息
  • --disable-passes=:禁止某些pass项,例:--disable-passes=UseCount,BBOptimizations
  • --print-pass-options:打印当前配置的pass信息
  • --pass-options=Pass1Name:Pass1OptionName:Pass1Option#,Pass2Name:Pass2OptionName:Pass2Option#:指定pass信息。

临时文件类

  • --swap-file=<交换文件名>:指定交换文件,例:--swap-file=/data/tmp/swap.001
  • --swap-fd=<文件描述符>:指定交换文件的描述符

使用示例:

/system/bin/dex2oat --zip-fd=6 --zip-location=/data/app/< Package Name >-1/base.apk 
--oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@< Package Name >-1@base.apk@classes.dex 
--instruction-set=arm --instruction-set-features=div --runtime-arg -Xms64m 
--runtime-arg -Xmx512m --swap-fd=8  

三、dex2oat编译优化

强制编译

dex2oat在Android中属于系统命令,因此我们无法使用去编译其他app,但是Android同样提供了通过PMS执行此命令的方式

要强制编译,请运行以下命令:

adb shell cmd package compile

强制编译特定软件包的常见用例:

  • 基于配置文件:
    adb shell cmd package compile -m speed-profile -f my-package
  • 全面:
    adb shell cmd package compile -m speed -f my-package

强制编译所有软件包的常见用例:

  • 基于配置文件:
    adb shell cmd package compile -m speed-profile -f -a
  • 全面:
    adb shell cmd package compile -m speed -f -a

编译插件的DEX

我们经常遇到热修复、插件话等情况,但是系统提供的命令无法支持编译插件或者补丁包,下面是一种可行的方式,只能编译app本身的

public class Dex2OatHelper {

    private final String TAG = "Dex2OatHelper";

    public void makeDex2OatV1(String dexFilePath, String oatFilePath) throws IOException {
        final File oatFile = new File(oatFilePath);
        if (!oatFile.exists()) {
            oatFile.getParentFile().mkdirs();
        }

        try {
            final List<String> commandAndParams = new ArrayList<>();
            commandAndParams.add("dex2oat");
            // for 7.1.1, duplicate class fix
            if (Build.VERSION.SDK_INT >= 24) {
                commandAndParams.add("--runtime-arg");
                commandAndParams.add("-classpath");
                commandAndParams.add("--runtime-arg");
                commandAndParams.add("&");
            }
            commandAndParams.add("--dex-file=" + dexFilePath);
            commandAndParams.add("--oat-fd=" + oatFilePath);
            commandAndParams.add("--instruction-set=" + Build.CPU_ABI);
            if (Build.VERSION.SDK_INT > 25) {
                commandAndParams.add("--compiler-filter=quicken");
            } else {
                commandAndParams.add("--compiler-filter=interpret-only");
            }

            final ProcessBuilder pb = new ProcessBuilder(commandAndParams);
            pb.redirectErrorStream(true);
            final Process dex2oatProcess = pb.start();
            StreamConsumer.consumeInputStream(dex2oatProcess.getInputStream());
            StreamConsumer.consumeInputStream(dex2oatProcess.getErrorStream());
            try {
                final int ret = dex2oatProcess.waitFor();
                if (ret != 0) {
                    throw new IOException("dex2oat works unsuccessfully, exit code: " + ret);
                }
            } catch (InterruptedException e) {
                throw new IOException("dex2oat is interrupted, msg: " + e.getMessage(), e);
            }
        } finally {

        }
    }

    public static boolean makeDex2OatV2(String dexFilePath, String oatFilePath){
        try {
            DexFile.loadDex(dexFilePath, oatFilePath, 0);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }
    private static class StreamConsumer {
        static final Executor STREAM_CONSUMER = Executors.newSingleThreadExecutor();

        static void consumeInputStream(final InputStream is) {
            STREAM_CONSUMER.execute(new Runnable() {
                @Override
                public void run() {
                    if (is == null) {
                        return;
                    }
                    final byte[] buffer = new byte[256];
                    try {
                        while ((is.read(buffer)) > 0) {
                            // To satisfy checkstyle rules.
                        }
                    } catch (IOException ignored) {
                        // Ignored.
                    } finally {
                        try {
                            is.close();
                        } catch (Exception ignored) {
                            // Ignored.
                        }
                    }
                }
            });
        }
    }

}

清除配置文件数据

要清除配置文件数据并移除经过编译的代码,请运行以下命令:

  • 针对一个软件包:
    adb shell cmd package compile --reset my-package
  • 针对所有软件包:
    adb shell cmd package compile --reset -a
  • 启动后台优化
adb shell cmd package bg-dexopt-job

以下是一段编译案例

D/dexOptimize: quicken开始优化,app总数1
I/PackageManager.DexOptimizer: Running dexopt (dexoptNeeded=1) on: /data/app/com.lwjfork.example-aSx3aJfLVJcl_MGpXnDDtg==/base.apk pkg=com.lwjfork.example isa=arm dexoptFlags=boot_complete,debuggable,public,enable_hidden_api_checks targetFilter=quicken oatDir=/data/app/com.lwjfork.example-aSx3aJfLVJcl_MGpXnDDtg==/oat classLoaderContext=PCL[]{PCL[/system/framework/org.apache.http.legacy.jar]}
D/dexOptimize: {0: package='com.lwjfork.example', appSize='0.0MB', isOptimized=true, costTime=4819}
D/dexOptimize: 优化完成,总计耗时:5154, 编译成功率:100.0%, 平均耗时:5154.0

代码层面

我们从PackageManagerShellCommand找到相关参数的用法

   pw.println("  compile [-m MODE | -r REASON] [-f] [-c]");
        pw.println("          [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)");
        pw.println("    Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".");
        pw.println("    Options:");
        pw.println("      -a: compile all packages");
        pw.println("      -c: clear profile data before compiling");
        pw.println("      -f: force compilation even if not needed");
        pw.println("      -m: select compilation mode");
        pw.println("          MODE is one of the dex2oat compiler filters:");
        pw.println("            assume-verified");
        pw.println("            extract");
        pw.println("            verify");
        pw.println("            quicken");
        pw.println("            space-profile");
        pw.println("            space");
        pw.println("            speed-profile");
        pw.println("            speed");
        pw.println("            everything");
        pw.println("      -r: select compilation reason");
        pw.println("          REASON is one of:");
        for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
            pw.println("            " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
        }
        pw.println("      --reset: restore package to its post-install state");
        pw.println("      --check-prof (true | false): look at profiles when doing dexopt?");
        pw.println("      --secondary-dex: compile app secondary dex files");

最终我们会调用到

PackageDexOptimizer中的performDex相关方法

这里会自动补充一些普通用户无法获取到的dex2oat的参数

    /**
     * Performs dexopt on all code paths and libraries of the specified package for specified
     * instruction sets.
     *
     * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
     * synchronized on {@link #mInstallLock}.
     */
    int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
            String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
            CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps) {
        if (!canOptimizePackage(pkg)) {
            return DEX_OPT_SKIPPED;
        }
        synchronized (mInstallLock) {
            final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
            try {
                return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
                        targetCompilationFilter, packageStats, isUsedByOtherApps);
            } finally {
                releaseWakeLockLI(acquireTime);
            }
        }
    }

    /**
     * Performs dexopt on all code paths of the given package.
     * It assumes the install lock is held.
     */
    @GuardedBy("mInstallLock")
    private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
            String[] targetInstructionSets, boolean checkForProfileUpdates,
            String targetCompilerFilter, CompilerStats.PackageStats packageStats,
            boolean isUsedByOtherApps) {
        final String[] instructionSets = targetInstructionSets != null ?
                targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
        final List<String> paths = pkg.getAllCodePaths();
        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);

        final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
                targetCompilerFilter, isUsedByOtherApps);
        final boolean profileUpdated = checkForProfileUpdates &&
                isProfileUpdated(pkg, sharedGid, compilerFilter);

        final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
        final int dexoptFlags = getDexFlags(pkg, compilerFilter);
        // Get the dependencies of each split in the package. For each code path in the package,
        // this array contains the relative paths of each split it depends on, separated by colons.
        String[] splitDependencies = getSplitDependencies(pkg);

        int result = DEX_OPT_SKIPPED;
        for (int i = 0; i < paths.size(); i++) {
            // Skip paths that have no code.
            if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) ||
                    (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) {
                continue;
            }
            // Append shared libraries with split dependencies for this split.
            String path = paths.get(i);
            String sharedLibrariesPathWithSplits;
            if (sharedLibrariesPath != null && splitDependencies[i] != null) {
                sharedLibrariesPathWithSplits = sharedLibrariesPath + ":" + splitDependencies[i];
            } else {
                sharedLibrariesPathWithSplits =
                        splitDependencies[i] != null ? splitDependencies[i] : sharedLibrariesPath;
            }
            for (String dexCodeIsa : dexCodeInstructionSets) {
                int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
                        sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats);
                // The end result is:
                //  - FAILED if any path failed,
                //  - PERFORMED if at least one path needed compilation,
                //  - SKIPPED when all paths are up to date
                if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
                    result = newResult;
                }
            }
        }
        return result;
    }

@GuardedBy("mInstallLock")
    private int dexOptPath(PackageParser.Package pkg, String path, String isa,
            String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated);
        if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
            return DEX_OPT_SKIPPED;
        }

        // TODO(calin): there's no need to try to create the oat dir over and over again,
        //              especially since it involve an extra installd call. We should create
        //              if (if supported) on the fly during the dexopt call.
        String oatDir = createOatDirIfSupported(pkg, isa);

        Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
                + " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
                + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
                + " target-filter=" + compilerFilter + " oatDir=" + oatDir
                + " sharedLibraries=" + sharedLibrariesPath);

        try {
            long startTime = System.currentTimeMillis();

            mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);

            if (packageStats != null) {
                long endTime = System.currentTimeMillis();
                packageStats.setCompileTime(path, (int)(endTime - startTime));
            }
            return DEX_OPT_PERFORMED;
        } catch (InstallerException e) {
            Slog.w(TAG, "Failed to dexopt", e);
            return DEX_OPT_FAILED;
        }
    }

接下来到Installer类的dexopt方法

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)
        throws InstallerException {
    assertValidInstructionSet(instructionSet);
    if (!checkBeforeRemote()) return;
    try {
        mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
                dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo);
    } catch (Exception e) {
        throw InstallerException.from(e);
    }
}

编译效果对比 文章来源地址https://www.toymoban.com/news/detail-853242.html

默认:
random :                160.0ms     200.0ms     160.0ms     190.0ms  200.0ms     平均:182ms
createApplication :     20.0ms      20.0ms      20.0ms      20.0ms   20.0ms      平均:20ms
recordReciever:         170.0ms     170.0ms     240.0ms     130.0ms  160.0ms     平均:174ms
createTestActivity :    860.0ms     400.0ms     560.0ms     380.0ms  370.0ms     平均:514ms
createMainActivity:     1060.0ms    980.0ms     1020.0ms    980.0ms  960.0ms     平均:1000ms
layoutResId :           1070.0ms    1030.0ms    1050.0ms    1090.0ms 1000.0ms    平均:1048ms
createTestService :     180.0ms     190.0ms     250.0ms     150.0ms  180.0ms     平均:170ms
md5:                    390.0ms     450.0ms     450.0ms     470.0ms  400.0ms     平均:432ms

appSize : 2.54MB

quicken模式
首次编译耗时:  5154ms

random :             200.0ms 210.0ms 220.0ms 150.0ms 190.0ms  平均:194ms
createApplication :  20.0ms  20.0ms  20.0ms  30.0ms  20.0ms   平均:22ms
recordReciever:      280.0ms 140.0ms 150.0ms 150.0ms 170.0ms  平均:178ms
createTestActivity : 620.0ms 380.0ms 470.0ms 350.0ms 420.0ms  平均:448ms
createMainActivity:  710.0ms 640.0ms 690.0ms 660.0ms 680.0ms  平均:676ms
layoutResId :        720.0ms 660.0ms 770.0ms 670.0ms 700.0ms  平均:704ms
createTestService :  270.0ms 150.0ms 180.0ms 160.0ms 160.0ms  平均:184ms
md5:                 460.0ms 400.0ms 490.0ms 390.0ms 480.0m   平均:444ms

appSize : 4.46MB

speed模式
首次编译耗时: 7125ms
random :             200.0ms 190.0ms 140.0ms 160.0ms 200.0ms 平均:178ms
createApplication :  20.0ms  20.0ms  30.0ms  20.0ms  20.0ms  平均:22ms
recordReciever:      160.0ms 150.0ms 150.0ms 120.0ms 130.0ms 平均:142ms
createTestActivity : 420.0ms 400.0ms 430.0ms 360.0ms 360.0ms 平均:394ms
createMainActivity:  720.0ms 780.0ms 670.0ms 680.0ms 680.0ms 平均:706ms
layoutResId :        680.0ms 710.0ms 650.0ms 640.0ms 660.0ms 平均:668ms
createTestService :  220.0ms 150.0ms 150.0ms 130.0ms 130.0ms 平均:156ms
md5:                 570.0ms 430.0ms 520.0ms 420.0ms 400.0ms 平均:468ms

appSize : 4.46MB


everything模式
首次编译耗时: 9020ms
random :             210.0ms 180.0ms 160.0ms 300.0ms 200.0ms  平均:210ms
createApplication :  20.0ms  20.0ms  20.0ms  20.0ms  20.0ms   平均:20ms
recordReciever:      190.0ms 160.0ms 160.0ms 190.0ms 150.0ms  平均:170ms
createTestActivity : 510.0ms 400.0ms 390.0ms 470.0ms 420.0ms  平均:438ms
createMainActivity:  670.0ms 680.0ms 770.0ms 710.0ms 730.0ms  平均:438ms
layoutResId :        670.0ms 670.0ms 670.0ms 680.0ms 680.0ms  平均:712ms
createTestService :  210.0ms 190.0ms 180.0ms 190.0ms 150.0ms  平均:184ms
md5:                 430.0ms 550.0ms 440.0ms 410.0ms 400.0ms  平均:446ms
appSize : 4.46MB

到了这里,关于Android 编译优化——dex2oat编译的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 反编译dex文件

    想要反编译dex文件可以通过一下步骤实现: 1、使用解压工具将apk包中的dex文件解压出来;   2、使用dex2jar工具将dex反编译为jar包; dex2jar工具的下载地址:dex2jar download | SourceForge.net     3、使用jd-gui工具打开解压出来的jar包; jd-gui工具下载地址:https://github.com/java-decompiler

    2024年02月11日
    浏览(34)
  • Android14之Android Rust模块编译语法(一百八十七)

    简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏: Audio工程师进阶系列 【 原创干货持续更新中…… 】🚀 优质专栏: 多媒体系统工程师系列 【 原创干货持续更新中…… 】🚀 人生格言: 人生从来没有捷径

    2024年02月22日
    浏览(76)
  • 由Android10适配到Android12时遇到相关编译失败问题

    最近Android系统各大应用商店联合发出公告,处于个人隐私安全考虑,强制APP适配到Android 11及以上版本。下面是其中应用市场的公告(顺带提醒没适配的同学): 适配前的开发环境 名称 版本 Android studio Giraffe | 2022.3.1 build.gradle配置的compileOptions JDK 1.8 build.gradle配置的kotlinOpt

    2024年02月11日
    浏览(42)
  • android 反编译工具使用

    记录一下dex2jar和ByteCode viewer的使用。 下载dex2jar 官方地址是https://github.com/pxb1988/dex2jar,下载完成后解压到特定的目录中,然后将其配置到环境变量中。 准备我们要反编译的apk,通过如下命令将apk反编译成jar文件 接下来就是查看jar文件的内容了,可视化工具也有不少,我使用

    2024年01月17日
    浏览(44)
  • Android 源码编译方法

    和你一起终身学 习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一、查看项目所在分支 二、切换到目标分支 三、查看当前所在分支 四、编译Android源码 五、source Android 编译环境 六、lunch 所需的编译项目 七、单编 模块 八、push 模块 验证修改是

    2024年02月07日
    浏览(48)
  • Android 系统源码编译

    Android系统源码编译网上流传着很多优秀的文章,为什么我要写这个呢,主要的原因是记录编译系统的过程,这里以Android9.0,手机设备Piexl一代为例来讲述编译和刷机过程。 ubuntu18.04 内存至少12G RAM 硬盘空间至少200GB (1) 安装python (2)安装Git 配置Git信息 (3)安装curl (4)

    2024年02月07日
    浏览(44)
  • Android Automotive编译

    准备一台安装Ubuntu系统的机器(windows系统的机器可以通过WSL安装ubuntu系统)   本文使用docker进行编译,因此提前安装docker。参考网络链接安装docker并设置为不使用sudo进行docker操作。 参考链接: Ubuntu22.04安装Docker_Canminem的博客-CSDN博客 不使用sudo 执行Docker命令的方法 -博客-

    2024年02月09日
    浏览(43)
  • Android编译Skia库

    本文档提供两种方法编译Skia库 使用aosp源码进行编译 使用skia源码进行编译 两种编译方法都可以编译,并且都可以使用在多个平台中,且可以使用在不同Android版本中 第一步是拿到aosp的源码.因为写这篇教程,使用的是msm8996的源码,因此,我就直接使用了. 第二步进入项目根目录,运行

    2024年02月15日
    浏览(34)
  • Android编译snowboy

    如果Ubuntu是新创建的,可以安装build-essential软件包 该命令将安装一堆新软件包,包括gcc,g++和make。 1.打开终端 在Ubuntu系统中,我们可以使用终端输入命令进行操作。打开终端的快捷键为Ctrl+Alt+T。 2.安装Git 在终端中输入以下命令,即可开始安装Git。 登录后复制 安装过程中需

    2024年02月07日
    浏览(34)
  • Android 编译介绍

            Android的源码非常的庞大,编译Android系统往往会占用我们很长的时间,我们需要了解下Android的编译规则,以期能提高我们的开发效率,达到程序员 按时下班 的伟大理想。 平台:QCM2290 版本:Android 11 编译服务器: 64G + 32核         由于Android各个层级之间的耦合

    2024年02月09日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包