Google 致力于为黑人社区推动种族平等。查看具体举措

优化构建速度

构建时间太长会拖慢您的开发过程。本页将介绍一些可以帮助您突破构建速度瓶颈的技巧。

提高构建速度的一般过程如下:

  1. 采取一些可以使大多数 Android Studio 项目立即受益的措施,优化构建配置
  2. 对构建进行性能剖析,确定并诊断一些对您的项目或工作站来说比较棘手的瓶颈问题。

开发应用时,您应尽可能将其部署到搭载 Android 7.0(API 级别 24)或更高版本的设备中。较新版本的 Android 平台有更出色的机制来向您的应用推送更新,例如 Android 运行时 (ART) 以及对多个 DEX 文件的原生支持。

注意:您完成首次干净构建后,可能会注意到后续构建(干净和增量)的执行速度明显加快了(即使您没有使用本页面介绍的任何优化措施)。这是因为 Gradle 守护程序有一个性能提升“预热”期,类似于其他 JVM 进程。

优化构建配置

按照下面的提示操作,以提高 Android Studio 项目的构建速度。

确保工具已是最新版本

几乎每次更新时,Android 工具都会获得构建方面的优化和新功能,本页介绍的一些提示假设您使用的是最新版本。为了充分利用最新的优化,请确保以下工具已是最新版本:

创建用于开发的构建变体

为应用发布做准备时所需的许多配置在应用开发过程中都是不必要的。启用不必要的构建流程会减慢您的增量构建和干净构建速度,因此,请配置一个构建变体,使之仅包含开发应用时所需的构建配置。以下代码示例为您的发布版本配置创建了一个“dev”变种和一个“prod”变种:

android {
  ...
  defaultConfig {...}
  buildTypes {...}
  productFlavors {
    // When building a variant that uses this flavor, the following configurations
    // override those in the defaultConfig block.
    dev {
      // To avoid using legacy multidex when building from the command line,
      // set minSdkVersion to 21 or higher. When using Android Studio 2.3 or higher,
      // the build automatically avoids legacy multidex when deploying to a device running
      // API level 21 or higher—regardless of what you set as your minSdkVersion.
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
    }

    prod {
      // If you've configured the defaultConfig block for the release version of
      // your app, you can leave this block empty and Gradle uses configurations in
      // the defaultConfig block instead. You still need to create this flavor.
      // Otherwise, all variants use the "dev" flavor configurations.
    }
  }
}

如果您的构建配置已使用产品变种创建应用的不同版本,您可以使用变种维度将“dev”和“prod”配置与这些变种组合起来。例如,如果您已经配置了“demo”和“full”变种,则可以使用以下示例配置创建组合变种,如“devDemo”和“prodFull”:

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.

  flavorDimensions "stage", "mode"

  productFlavors {
    dev {
      dimension "stage"
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
      ...
    }

    prod {
      dimension "stage"
      ...
    }

    demo {
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }
  }
}

启用单变体项目同步

将项目与构建配置同步,是让 Android Studio 了解项目结构的重要步骤。不过,对于大型项目来说,此过程可能非常耗时。如果您的项目使用了多个构建变体,那么您现在可以限定仅同步当前所选的变体,从而优化项目同步操作。

您需要结合使用 Android Studio 3.3 或更高版本与 Android Gradle 插件 3.3.0 或更高版本,才能启用此优化。所有项目默认都启用了此优化。

如需手动启用此优化功能,请依次点击 File > Settings > Experimental > Gradle(在 Mac 上,请依次点击 Android Studio > Preferences > Experimental > Gradle),然后选中 Only sync the active variant 复选框。

注意:此优化完全支持包含 Java 和 C++ 语言的项目,部分支持包含 Kotlin 语言的项目。在为包含 Kotlin 内容的项目启用此优化时,Gradle 同步会回退到在内部使用完整的变体。

避免编译不必要的资源

避免编译和打包不测试的资源(例如,其他语言本地化和屏幕密度资源)。为此,您可以仅为“dev”变种的版本指定一个语言资源和屏幕密度,如下面的示例中所示:

android {
  ...
  productFlavors {
    dev {
      ...
      // The following configuration limits the "dev" flavor to using
      // English stringresources and xxhdpi screen-density resources.
      resConfigs "en", "xxhdpi"
    }
    ...
  }
}

对调试 build 停用 Crashlytics

如果您不需要运行 Crashlytics 报告,请按如下方法停用该插件,以提高调试 build 的构建速度:

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false
    }
}

此外,您还需要通过更改在应用中初始化对 Fabric 的支持的方式,在运行时为调试 build 停用 Crashlytics 套件,如下所示:

Kotlin

// Initializes Fabric for builds that don't use the debug build type.
Crashlytics.Builder()
        .core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
        .build()
        .also { crashlyticsKit ->
            Fabric.with(this, crashlyticsKit)
        }

Java

// Initializes Fabric for builds that don't use the debug build type.
Crashlytics crashlyticsKit = new Crashlytics.Builder()
    .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
    .build();

Fabric.with(this, crashlyticsKit);

禁止自动生成 build ID

如果您想要将 Crashlytics 用于调试 build,仍可以通过阻止 Crashlytics 在每次构建过程中使用自己的唯一 build ID 更新应用资源,提高增量构建的速度。由于此 build ID 存储在清单引用的资源文件中,因此禁止自动生成 build ID 还可以将 Apply Changes 和 Crashlytics 一起用于您的调试 build。

如需阻止 Crashlytics 自动更新其 build ID,请将以下内容添加到您的 build.gradle 文件中:

android {
  ...
  buildTypes {
    debug {
      ext.alwaysUpdateBuildId = false
    }
}

如需详细了解如何在使用 Crashlytics 时优化您的构建过程,请参阅官方文档

将静态构建配置值用于调试 build

始终为会进入调试 build 类型的清单文件或资源文件的属性使用静态/硬编码值。

例如,您每次需要运行更改时,要使用动态版本代码、版本名称、资源或可以更改清单文件的任何其他构建逻辑,都需要完整的 APK build,即使实际更改可能仅需要一次热交换,也是如此。如果您的构建配置需要此类动态属性,请将其隔离到您的发布 build 变体中,并使该值对您的调试 build 保持静态,如下面的 build.gradle 文件所示。

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full APK build and reinstallation because the AndroidManifest.xml
        // must be updated.
        versionCode 1
        versionName "1.0"
        ...
    }

    // The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest (and therefore the whole APK, slowing build times).
    // But for release builds, it's okay. So the following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.all { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch;
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
        }
    }
}

使用静态依赖项版本

build.gradle 文件中声明依赖项时,您应当避免在结尾处使用带加号的版本号,例如 'com.android.tools.build:gradle:2.+'。使用动态版本号可能会导致意外的版本更新和难以解析版本差异,并会因 Gradle 检查有无更新而减慢构建速度。您应该使用静态/硬编码版本号。

启用离线模式

如果网络连接速度比较慢,那么在 Gradle 尝试使用网络资源解析依赖项时,构建时间可能会延长。您可以指示 Gradle 仅使用已缓存到本地的工件,从而避免使用网络资源。

如需在使用 Android Studio 构建项目时离线使用 Gradle,请按以下步骤操作:

  1. 依次点击 File > Settings(在 Mac 上,则依次点击 Android Studio > Preferences),打开 Preferences 窗口。
  2. 在左侧窗格中,依次点击 Build, Execution, Deployment > Gradle
  3. 勾选 Offline work 复选框。
  4. 点击 ApplyOK

如果您正在通过命令行构建,请传递 --offline 选项。

创建库模块

在应用中查找可以转换成 Android 库模块的代码。以这种方式将您的代码模块化,可以让构建系统仅编译您修改的模块,并缓存输出以用于未来的构建。此外,这种方式也会让并行项目执行更有效(当您启用该优化时)。

为自定义构建逻辑创建任务

创建构建性能剖析报告后,如果性能剖析报告显示相当长的一部分构建时间用在了“配置项目”阶段,请检查 build.gradle 脚本并查找您可以添加到自定义 Gradle 任务中的代码。将某些构建逻辑移到任务中后,它仅会在需要时运行,可以缓存结果以用于后续构建,并且该构建逻辑将可以并行运行(如果您已启用并行项目执行)。如需了解详情,请阅读官方 Gradle 文档

提示:如果您的构建包含大量自定义任务,您可能需要通过创建自定义任务类来整理 build.gradle 文件。将您的类添加到 project-root/buildSrc/src/main/groovy/ 目录中,Gradle 会自动将其添加到项目中所有 build.gradle 文件的类路径中。

将图片转换为 WebP 格式

WebP 是一种既可以提供有损压缩(像 JPEG 一样)也可以提供透明度(像 PNG 一样)的图片文件格式,不过与 JPEG 或 PNG 相比,这种格式可以提供更好的压缩。减小图片文件大小可以加快构建速度(无需在构建时进行压缩),尤其是当应用使用大量图片资源时。不过,在解压缩 WebP 图片时,您可能会注意到设备的 CPU 使用率有小幅上升。通过使用 Android Studio,您可以轻松地将图片转换为 WebP 格式

停用 PNG 处理

如果您无法(或者不想)将 PNG 图像转换为 WebP 格式,仍可以在每次构建应用时停用自动图片压缩,从而提高构建速度。如果您使用的是 Android 插件 3.0.0 或更高版本,默认情况下仅针对“调试”构建类型停用 PNG 处理。如需针对其他构建类型停用此优化,请将以下代码添加到 build.gradle 文件中:

android {
    buildTypes {
        release {
            // Disables PNG crunching for the release build type.
            crunchPngs false
        }
    }

// If you're using an older version of the plugin, use the
// following:
//  aaptOptions {
//      cruncherEnabled false
//  }
}

由于构建类型或产品变种不定义此属性,因此在构建应用的发布版本时,您需要手动将此属性设置为 true

启用构建缓存

构建缓存可以存储构建项目时 Android Plugin for Gradle 生成的特定输出(例如,未打包的 AAR 和经过 dex 预处理的远程依赖项)。使用缓存时,干净构建的速度会显著加快,因为构建系统在进行后续构建时可以直接重用这些缓存的文件,而无需重新创建。

使用 Android 插件 2.3.0 及更高版本的新项目会默认启用构建缓存(除非您明确停用构建缓存)。如需了解详情,请参阅利用构建缓存加快干净构建的速度

使用增量注释处理器

Android Gradle 插件 3.3.0 及更高版本改进了对增量注释处理的支持。因此,如需提高增量构建速度,您应更新 Android Gradle 插件并尽可能仅使用增量注释处理器。

注意:此功能与 Gradle 4.10.1 及更高版本(Gradle 5.1 除外)兼容(请参阅 Gradle 问题 8194)。

首先,参阅下表,了解支持增量注释处理的常见注释处理器。如需更完整的列表,请参阅常见注释处理器中的支持状态。某些注释处理器可能需要额外的步骤才能启用优化功能,因此请务必阅读每种注释处理器的文档。

此外,如果您在应用中使用 Kotlin,就需要使用 kapt 1.3.30 及更高版本才能在 Kotlin 代码中支持增量注释处理器。请务必阅读有关是否需要手动启用此行为的官方文档。

请注意,如果您必须使用一个或多个不支持增量构建的注释处理器,注释处理将不会是增量的。但是,如果您的项目使用的是 kapt,Java 编译仍然是增量的。

增量注释处理器支持

项目名称注释处理器类名称支持增量处理器的起始版本…
DataBindingandroid.databinding.annotationprocessor.ProcessDataBindingAGP 3.5
Roomandroidx.room.RoomProcessor2.3.0-alpha02

2.20:使用 room.incremental 选项
ButterKnifebutterknife.compiler.ButterKnifeProcessor10.2.0
Glidecom.bumptech.glide.annotation.compiler.GlideAnnotationProcessor4.9.0
Daggerdagger.internal.codegen.ComponentProcessor2.18
Lifecycleandroidx.lifecycle.LifecycleProcessor2.2.0-alpha02
AutoServicecom.google.auto.service.processor.AutoServiceProcessor1.0-rc7
Daggerdagger.android.processor.AndroidProcessor2.18
Realmio.realm.processor.RealmProcessor5.11.0
Lomboklombok.launch.AnnotationProcessorHider$AnnotationProcessor1.16.22
Lomboklombok.launch.AnnotationProcessorHider$ClaimingProcessor1.16.22

对构建进行性能剖析

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

如果您使用的是 Android Studio 4.0 或更高版本,则最适合使用 Build 分析器调查构建性能问题。

从命令行对构建进行性能剖析

如果您未使用 Android Studio,则要排查构建速度问题,通常需要通过启用剖析的命令行运行构建,对构建配置进行一些调整,以及执行更多性能剖析来观察更改的结果。

如需生成和查看构建性能剖析报告,请按以下步骤操作:

  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. 查看任务执行结果。

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

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

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

提示:如需更强大的性能剖析工具,请考虑使用 Gradle 的开源代码性能剖析器