剖析您的 build

较大的项目或实现了大量自定义 build 逻辑的项目,可能需要您深入了解 build 过程以查找瓶颈。您可以通过剖析 Gradle 执行 build 生命周期每个阶段和每个 build 任务所需的时间来做到这一点。例如,如果您的 build 剖析显示 Gradle 花费太多时间配置项目,这可能表明您需要将自定义 build 逻辑移出配置阶段。此外,如果 mergeDevDebugResources 任务占用了大量的 build 时间,这可能表明您需要将图片转换为 WebP停用 PNG 压缩

如果您使用的是 Android Studio 4.0 或更高版本,调查 build 性能问题的最佳方式是使用 Build Analyzer

此外,在 Android Studio 之外剖析您的 build 还有两种选项:

  1. 独立的 gradle-profiler 工具,一个用于深度分析您的 build 的强大工具。

  2. Gradle --profile 选项,一个可从 Gradle 命令行获取的便捷工具。

使用独立的 gradle-profiler 工具

为了找到能为您提供最佳 build 速度的项目设置,您应该使用 Gradle profiler,这是一个用于收集 Gradle build 剖析和基准测试信息的工具。Gradle profiler 允许您创建 build 场景并多次运行它们,从而防止结果之间出现较大的差异,并确保结果的可重现性。

基准测试模式应 F于收集关于 clean build 和增量 build 的信息,而剖析模式可用于收集关于运行的更精细的信息,包括 CPU 快照。

某些用于基准测试的项目设置配置包括:

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

开始使用

  • 按照这些说明安装 gradle-profiler
  • 运行:gradle-profiler --benchmark --project-dir <root-project> :app:assembleDebug

这将对一个完全最新的 build 进行基准测试,因为 --benchmark 会多次运行任务,而不会在任务之间更改项目。然后,它将在 profile-out/ 目录下生成一个 HTML 报告,显示 build 时间。

还有其他可能更有用的基准测试场景:

  • 在您大部分工作所在的类的某个方法体中进行代码更改。
  • 在整个项目中使用的模块中的 API 更改。虽然这比更改您自己的代码不那么频繁,但它影响更大,测量它很有用。
  • 布局编辑,用于模拟 UI 工作的迭代。
  • 字符串编辑,用于模拟处理翻译工作。
  • Clean build,用于模拟 build 本身的变化(例如,Android Gradle 插件更新、Gradle 更新,或对 buildSrc 下您自己的 build 代码进行编辑)。

为了对这些用例进行基准测试,您可以创建一个场景,该场景将用于驱动 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 时间,请为此创建一个基准测试场景。这需要一些准备工作,以使插件版本可以从场景中注入。更改您的根 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"
}

剖析增量 build

大多数 build 都是增量 build,这使其成为最重要的剖析场景之一。Gradle profiler 广泛支持剖析增量 build。它能够通过更改方法体、添加新方法或更改布局或字符串资源来自动对源文件应用更改。例如,您可以创建如下所示的增量场景:

# <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 报告。

您可以将增量场景与其他设置结合使用,例如堆大小、worker 数量或 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"]
}

剖析 clean build

为了对 clean build 进行基准测试,您可以创建一个将用于驱动 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 命令行生成和查看 build 剖析,请执行以下步骤:

  1. 在项目根目录打开命令行终端。
  2. 输入以下命令执行 clean build。在剖析 build 时,您应该在每次剖析的 build 之间执行 clean build,因为当任务的输入(例如源代码)没有更改时,Gradle 会跳过任务。因此,没有输入更改的第二次 build 总是运行得更快,因为任务没有被重新运行。因此,在您的 build 之间运行 clean 任务可确保您剖析完整的 build 过程。
    // 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. 显示剖析报告位置的项目视图。

    build 完成后,使用项目窗口导航到 project-root/build/reports/profile/ 目录(如图 1 所示)。

  5. 右键点击 profile-timestamp.html 文件,然后选择在浏览器中打开 > 默认。报告应类似于图 2 所示。您可以检查报告中的每个选项卡,以了解您的 build,例如任务执行选项卡显示 Gradle 执行每个 build 任务所需的时间。

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

  6. 可选:在对您的项目或 build 配置进行任何更改之前,重复第 3 步中的命令,但省略 --rerun-tasks 标志。因为 Gradle 尝试通过不重新执行输入未更改的任务来节省时间(这些在报告的任务执行选项卡中显示为 UP-TO-DATE,如图 3 所示),您可以识别哪些任务在不应该运行时仍在执行工作。例如,如果 :app:processDevUniversalDebugManifest 未标记为 UP-TO-DATE,则可能表明您的 build 配置在每次 build 时都在动态更新清单。但是,某些任务需要在每次 build 期间运行,例如 :app:checkDevDebugManifest

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

现在您有了 build 剖析报告,可以通过检查报告中每个选项卡的信息来开始寻找优化机会。某些 build 设置需要进行实验,因为其益处可能因项目和工作站而异。例如,代码库较大的项目可能会受益于代码压缩,以移除未使用的代码并缩小应用大小。但是,较小的项目可能从完全停用代码压缩中受益更多。此外,增加 Gradle 堆大小(使用 org.gradle.jvmargs)可能会对低内存机器的性能产生负面影响。

对您的 build 配置进行更改后,通过重复上述步骤并生成新的 build 剖析来观察更改的结果。例如,图 4 显示了在应用本页面所述的一些基本优化后,相同示例应用的报告。

图 4. 优化 build 速度后查看新报告。