分析构建性能

对于较大的项目或者实现大量自定义构建逻辑的项目,您可能需要更深入地了解构建流程才能找到瓶颈。为此,您可以分析 Gradle 执行构建生命周期的每个阶段和每个构建任务所需的时间。例如,如果构建分析结果显示 Gradle 在配置项目上花费了过多时间,则表明您需要将自定义构建逻辑移出配置阶段。此外,如果 mergeDevDebugResources 任务占用了大量构建时间,则表明您需要将图片转换为 WebP 格式停用 PNG 处理

如果您使用的是 Android Studio 4.0 或更高版本,则最好使用 Build Analyzer 来调查构建性能问题。

此外,您还可以通过以下两种方式在 Android Studio 之外对构建性能进行分析:

  1. 独立的 gradle-profiler 工具,这是一款用于深入分析构建的强大工具。

  2. Gradle --profile 选项,这是一款可从 Gradle 命令行启动的便捷工具。

使用独立的 gradle-profiler 工具

为找到能够提供最快构建速度的项目设置,您需要使用 Gradle 性能剖析器,这是一款用于收集 Gradle 构建的性能分析和基准化分析信息的工具。借助 Gradle 性能剖析器,您可以创建构建场景并多次运行这些场景,以防止结果出现过大差异,并确保结果的可重现性。

基准化分析模式应用于收集干净构建和增量构建的相关信息,而性能分析模式则可用于收集更细化的运行相关信息,包括 CPU 快照。

基准化分析的部分项目设置配置包括:

  • 插件版本
  • Gradle 版本
  • JVM 设置(堆大小、永久代大小、垃圾回收等)
  • Gradle 工作器数量 (org.gradle.workers.max)
  • 按插件选项进一步优化性能

使用入门

  • 相关说明安装 gradle-profiler
  • 运行:gradle-profiler --benchmark --project-dir <root-project> :app:assembleDebug

这将对已完全更新的构建进行基准化分析,因为 --benchmark 会多次运行该任务,且在两次运行之间不会更改项目。然后,它会在 profile-out/ 目录下生成显示构建时间的 HTML 报告。

此外,还有其他场景可能更适合进行基准化分析:

  • 在某个类(您在其中完成大部分工作)中的方法正文中进行代码更改。
  • 在贯穿整个项目的模块中进行 API 更改。虽然这比更改您自己的代码的频率要低,但它的影响更大,因此针对此项的检测会很有帮助。
  • 修改布局,以模拟界面工作迭代。
  • 修改字符串,以模拟转换工作处理。
  • 干净构建,以模拟对构建本身的更改(例如,更新 Android Gradle 插件、更新 Gradle 或修改 buildSrc 下您自己的构建代码)。

要对这些用例进行基准化分析,您可以创建一个能够推动 gradle-profiler 执行的场景,用于对源代码文件应用相应的更改。您可以检测下面的一些常见场景。

针对不同的内存/CPU 设置执行性能分析

要针对不同的内存和 CPU 设置执行基准化分析,您可以创建使用不同 org.gradle.jvmargs 值的多种场景。例如,您可以创建以下场景:

# <root-project>/scenarios.txt
clean_build_2gb_4workers {
    tasks = [":app:assembleDebug"]
    gradle-args = ["--max-workers=4"]
    jvm-args = ["-Xmx2048m"]
    cleanup-tasks = ["clean"]
}
clean_build_parallelGC {
    tasks = [":app:assembleDebug"]
    jvm-args = ["-XX:+UseParallelGC"]
    cleanup-tasks = ["clean"]
}

clean_build_G1GC_4gb {
    tasks = [":app:assembleDebug"]
    jvm-args = ["-Xmx4096m", "-XX:+UseG1GC"]
    cleanup-tasks = ["clean"]
}

运行 gradle-profiler --benchmark --project-dir <root-project> --scenario-file scenarios.txt 即可运行这三种场景,并且,您还可以比较在上述每种设置下 :app:assembleDebug 的用时。

针对不同的 Gradle 插件版本进行性能分析

要了解更改 Gradle 插件版本会对构建时间产生什么影响,可以创建一个场景来对此进行基准化分析。为了能够从场景注入插件版本,您需要完成一些准备工作。更改您的根 build.gradle:

# <root-project>/build.gradle
buildscript {
    def agpVersion = providers.systemProperty("agpVersion").forUseAtConfigurationTime().orNull ?: '4.1.0'

    ext.kotlin = providers.systemProperty('kotlinVersion').forUseAtConfigurationTime().orNull ?: '1.4.0'

    dependencies {
        classpath "com.android.tools.build:gradle:$agpVersion"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin"
    }
}

现在,您即可在场景文件中指定 Android Gradle 插件和 Kotlin Gradle 插件的版本,还可以让场景向源代码文件添加新的方法:

# <root-project>/scenarios.txt
non_abi_change_agp4.1.0_kotlin1.4.10 {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    System-properties {
      "agpVersion" = "4.1.0"
      "kotlinVersion" = "1.4.10"
}

non_abi_change_agp4.2.0_kotlin1.4.20 {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    System-properties {
      "agpVersion" = "4.2.0-alpha16"
      "kotlinVersion" = "1.4.20"
}

对增量构建进行性能分析

大多数构建都是增量构建,因此该场景是您要进行性能分析的最重要的场景之一。Gradle 性能剖析器可为对增量构建进行性能分析提供广泛支持。可以通过更改方法正文、添加新方法、更改布局或字符串资源等方式,自动将更改应用于源代码文件。例如,您可以创建类似如下的增量场景:

# <root-project>/scenarios.txt
non_abi_change {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to = ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
}

abi_change {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to = ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
}

layout_change {
    tasks = [":app:assembleDebug"]
    apply-android-layout-change-to = "app/src/main/res/your_layout_file.xml"
}
string_resource_change {
    tasks = [":app:assembleDebug"]
    apply-android-resource-value-change-to = "app/src/main/res/values/strings.xml"
}

运行 gradle-profiler --benchmark --project-dir &lt;root-project> --scenario-file scenarios.txt 会生成包含基准化分析数据的 HTML 报告。

您可以结合其他设置来分析增量场景(如堆大小、工作器数量或 Gradle 版本):

# <root-project>/scenarios.txt
non_abi_change_4g {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx4096m"]
}

non_abi_change_4g_8workers {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx4096m"]
    gradle-args = ["--max-workers=8"]
}

non_abi_change_3g_gradle67 {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx3072m"]
    version = ["6.7"]
}

对干净构建进行性能分析

要对干净构建进行基准化分析,您可以创建一个推动 gradle-profiler 执行的场景:

# <root-project>/scenarios.txt
clean_build {
    tasks = [":app:assembleDebug"]
    cleanup-tasks = ["clean"]
}

如需运行此场景,请使用以下命令:

gradle-profiler --benchmark --project-dir <root-project> --scenario-file scenarios.txt

使用 Gradle --profile 选项

如需从 Gradle 命令行生成和查看构建性能分析报告,请按以下步骤操作:

  1. 在项目根目录下开启一个命令行终端。
  2. 输入以下命令,以执行干净构建。在剖析 build 性能时,您应该在剖析的每个 build 之间执行干净构建,这是因为如果某个任务的输入内容(例如源代码)未发生更改,Gradle 就会跳过它。因此输入内容未发生更改的第二个 build 始终会以更快的速度运行,因为任务不会重复运行。在 build 之间运行 clean 任务可以确保您能够剖析完整的构建流程。
    // On Mac or Linux, run the Gradle wrapper using "./gradlew".
    gradlew clean
    
  3. 使用以下标记为您的某个产品变种(例如“dev”变种)执行调试 build:
    gradlew --profile --offline --rerun-tasks assembleFlavorDebug
    
    • --profile:启用性能剖析。
    • --offline:禁止 Gradle 提取在线依赖项。这样可以确保因 Gradle 尝试更新依赖项而导致的任何延迟都不会干扰您的性能剖析数据。您应该已将项目构建一次,以确保 Gradle 已经下载和缓存您的依赖项。
    • --rerun-tasks:强制 Gradle 重新运行所有任务并忽略任何任务优化。
  4. 图 1. 指示性能剖析报告所在位置的 Project 视图。

    构建完成后,使用 Project 窗口转到 project-root/build/reports/profile/ 目录(如图 1 所示)。

  5. 右键点击 profile-timestamp.html 文件,然后依次选择 Open in Browser > Default。报告应与图 2 中显示的类似。您可以查看报告中的每个标签页以了解您的构建,例如,Task Execution 标签页显示了 Gradle 执行各个构建任务所花费的时间。

    图 2. 在浏览器中查看报告。

  6. 可选:在对您的项目或构建配置进行任何更改之前,请重复执行第 3 步中的命令,但请省略 --rerun-tasks 标记。由于 Gradle 会尝试通过不重复执行输入未发生变化的任务来节省时间(这些任务在报告的 Task Execution 标签页中标记为 UP-TO-DATE,如图 3 中所示),您可以确定哪些任务在不必要的时间执行了工作。例如,如果 :app:processDevUniversalDebugManifest 未标记为 UP-TO-DATE,可能表明您的构建配置会随每次构建动态更新清单文件。不过,有些任务(例如 :app:checkDevDebugManifest)需要在每次构建期间都运行。

    图 3. 查看任务执行结果。

现在,您已经有了一份构建性能剖析报告,可以通过查看报告中每个标签页下的信息发现优化机会。有些 build 设置需要进行实验,因为其优势在不同项目和工作站之间可能有所不同。例如,包含大型代码库的项目可能会受益于通过代码缩减移除不使用的代码并缩减应用大小。但是,项目越小,完全停用代码缩减功能带来的益处就越多。此外,增加 Gradle 堆的大小(使用 org.gradle.jvmargs)可能会对内存较小的机器的性能产生负面影响。

对构建配置进行更改后,请重复上述步骤并生成新的构建性能剖析报告,以观察更改的结果。例如,图 4 显示了同一示例应用在采纳本页介绍的一些基本优化后的报告。

图 4. 查看优化构建速度后的新报告。