构建时间太长会拖慢您的开发过程。本页将介绍一些可以帮助您突破构建速度瓶颈的技巧。
提高构建速度的一般过程如下:
- 采取一些可以使大多数 Android Studio 项目立即受益的措施,优化 build 配置。
- 对构建进行性能剖析,确定并诊断一些对您的项目或工作站来说比较棘手的瓶颈问题。
开发应用时,您应尽可能将其部署到搭载 Android 7.0(API 级别 24)或更高版本的设备中。较新版本的 Android 平台有更出色的机制来向您的应用推送更新,例如 Android 运行时 (ART) 以及对多个 DEX 文件的原生支持。
注意:您完成首次干净构建后,可能会注意到后续构建(干净和增量)的执行速度明显加快了(即使您没有使用本页面介绍的任何优化措施)。这是因为 Gradle 守护程序有一个性能提升“预热”期,类似于其他 JVM 进程。
优化 build 配置
按照下面的提示操作,以提高 Android Studio 项目的构建速度。
确保工具已是最新版本
几乎每次更新时,Android 工具都会获得构建方面的优化和新功能,本页介绍的一些提示假设您使用的是最新版本。为了充分利用最新的优化措施,请确保以下工具已是最新版本:
创建用于开发的 build 变体
为应用发布做准备时所需的许多配置在应用开发过程中都是不必要的。启用不必要的构建流程会减慢增量 build 和干净 build,因此,请配置一个 build 变体,使之仅包含开发应用时所需的 build 配置。以下代码示例为您的发布版本配置创建了一个“dev”变种和一个“prod”变种:
Groovy
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. } } }
Kotlin
android { ... defaultConfig {...} buildTypes {...} productFlavors { // When building a variant that uses this flavor, the following configurations // override those in the defaultConfig block. create("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" } create("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. } } }
如果您的 build 配置已使用产品变种创建应用的不同版本,您可以使用变种维度将“dev”和“prod”配置与这些变种组合起来。例如,如果您已经配置了“demo”和“full”变种,则可以使用以下示例配置创建组合变种,如“devDemo”和“prodFull”:
Groovy
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" ... } } }
Kotlin
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 { create("dev") { dimension = "stage" minSdkVersion(21) versionNameSuffix = "-dev" applicationIdSuffix = ".dev" ... } create("prod") { dimension = "stage" ... } create("demo") { dimension = "mode" ... } create("full") { dimension = "mode" ... } } }
单变体项目同步
将项目与 build 配置同步,是让 Android Studio 了解项目结构的重要步骤。不过,对于大型项目来说,此过程可能非常耗时。如果您的项目使用了多个 build 变体,那么 Android Studio 只会限制您当前选择的变体,从而优化项目同步操作。
此优化功能默认已在所有项目中启用,不能在 Android Studio 4.2 及更高版本中进行配置。
如需手动启用此优化功能,您需要使用 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”变种的版本指定一个语言资源和屏幕密度,如下面的示例中所示:
Groovy
android { ... productFlavors { dev { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resConfigs "en", "xxhdpi" } ... } }
Kotlin
android { ... productFlavors { create("dev") { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resConfigs("en", "xxhdpi") } ... } }
对调试 build 停用 Crashlytics
如果您不需要运行 Crashlytics 报告,请按如下方法停用该插件,以提高调试 build 的构建速度:
Groovy
android { ... buildTypes { debug { ext.enableCrashlytics = false } } }
Kotlin
android { ... buildTypes { getByName("debug") { extra["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
文件中:
Groovy
android { ... buildTypes { debug { ext.alwaysUpdateBuildId = false } } }
Kotlin
android { ... buildTypes { getByName("debug") { extra["alwaysUpdateBuildId"] = false } } }
如需详细了解如何在使用 Crashlytics 时优化您的构建过程,请参阅官方文档。
将静态 build 配置值用于调试 build
始终为会进入调试 build 类型的清单文件或资源文件的属性使用静态/硬编码值。
例如,您每次需要运行更改时,要使用动态版本代码、版本名称、资源或可以更改清单文件的任何其他构建逻辑,都需要完整的应用 build,即使实际更改可能仅需要一次热交换,也是如此。如果您的 build 配置需要此类动态属性,请将其隔离到您的发布 build 变体中,并使该值对您的调试 build 保持静态。有关示例,请参阅下面的 build.gradle
文件。
Groovy
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 app 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 app, 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; } } }
Kotlin
val MILLIS_IN_MINUTE = 1000 * 60 val minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE android { ... defaultConfig { // Making either of these two values dynamic in the defaultConfig will // require a full app 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 app, 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.forEach { 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 检查有无更新而减慢构建速度。您应该使用静态/硬编码版本号。
创建库模块
在应用中查找可以转换成 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 或更高版本,默认情况下仅针对“调试”build 类型停用 PNG 处理。如需针对其他 build 类型停用此优化,请将以下代码添加到 build.gradle
文件中:
Groovy
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 // } }
Kotlin
android { buildTypes { getByName("release") { // Disables PNG crunching for the release build type. isCrunchPngs = false } } // If you're using an older version of the plugin, use the // following: // aaptOptions { // cruncherEnabled = false // } }
由于 build 类型或产品变种不定义此属性,因此在构建应用的发布版本时,您需要手动将此属性设置为 true
。
使用增量注解处理器
Android Gradle 插件 3.3.0 及更高版本改进了对增量注解处理的支持。因此,如需提高增量构建速度,您应更新 Android Gradle 插件并尽可能仅使用增量注解处理器。
注意:此功能与 Gradle 4.10.1 及更高版本(Gradle 5.1 除外)兼容(请参阅 Gradle 问题 8194)。
首先,参阅下表,了解支持增量注解处理的常见注解处理器。如需更完整的列表,请参阅常见注释处理器中的支持状态。某些注解处理器可能需要额外的步骤才能启用优化,因此请务必阅读每种注解处理器的文档。
此外,如果您在应用中使用 Kotlin,就需要使用 kapt 1.3.30 及更高版本才能在 Kotlin 代码中支持增量注解处理器。请务必阅读有关是否需要手动启用此行为的官方文档。请注意,如果您必须使用一个或多个不支持增量构建的注释处理器,注释处理将不会是增量的。但是,如果您的项目使用的是 kapt,Java 编译仍然是增量的。
增量注释处理器支持
项目名称 | 注释处理器类名称 | 支持增量处理器的起始版本… |
---|---|---|
DataBinding | android.databinding.annotationprocessor.ProcessDataBinding | AGP 3.5 |
Room | androidx.room.RoomProcessor | 2.3.0-alpha02
2.20:使用 room.incremental 选项。
|
ButterKnife | butterknife.compiler.ButterKnifeProcessor | 10.2.0 |
Glide | com.bumptech.glide.annotation.compiler.GlideAnnotationProcessor | 4.9.0 |
Dagger | dagger.internal.codegen.ComponentProcessor | 2.18 |
Lifecycle | androidx.lifecycle.LifecycleProcessor | 2.2.0-alpha02 |
AutoService | com.google.auto.service.processor.AutoServiceProcessor | 1.0-rc7 |
Dagger | dagger.android.processor.AndroidProcessor | 2.18 |
Realm | io.realm.processor.RealmProcessor | 5.11.0 |
Lombok | lombok.launch.AnnotationProcessorHider$AnnotationProcessor | 1.16.22 |
Lombok | lombok.launch.AnnotationProcessorHider$ClaimingProcessor | 1.16.22 |
配置 JVM 垃圾回收器
通过配置 Gradle 所用的最佳 JVM 垃圾回收器,可以提升构建性能。虽然 JDK 8 默认配置为使用并行垃圾回收器,JDK 9 及更高版本已配置为使用 G1 垃圾回收器。
为提高构建性能,我们建议您使用并行垃圾回收器测试 Gradle 构建。在 gradle.properties
中设置以下内容:
org.gradle.jvmargs=-XX:+UseParallelGC
如果此字段中已设置了其他选项,请添加一个新选项:
org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC
如需使用不同的配置检测构建速度,请参阅对 build 进行性能分析。
使用非传递 R 类
您应使用非传递 R 类,以便让具有多个模块的应用获得更快的 build。这样做有助于确保每个模块的 R 类仅包含对其自身资源的引用,而不会从其依赖项中提取引用,从而帮助防止资源重复。这样可以获得更快的 build,以及避免编译的相应优势。
从 Android Studio Bumblebee 开始,新项目的非传递 R 类默认处于开启状态。对于使用早期版本的 Studio 创建的项目,您可以依次前往 Refactor > Migrate to Non-transitive R Classes,将项目更新为使用非传递 R 类。
如需详细了解应用资源和 R 类,请参阅应用资源概览。