长构建时间会减慢您的开发过程,因此本页面将介绍一些可以帮助您解决构建速度瓶颈的技巧。
改进您的构建速度的一般过程如下所示:
在开发您的应用时,如果可以,您应部署到正在运行 Android 7.0(API 级别 24)或更高版本的设备。较新版本的 Android 平台实现更好的机制来向您的应用推送更新,例如 Android 运行时 (ART) 以及对多个 DEX 文件的原生支持。
注:在您的首个干净构建后,您可能会注意到后续构建(干净和增量)的执行速度明显加快(即使没有使用本页介绍的任何优化措施,也是如此)。这是因为 Gradle 后台程序有一个提升性能的“预热”期 - 与其他 JVM 进程类似。
优化您的构建配置
按照下面的提示操作,提升您的 Android Studio 项目的构建速度。
保持工具处于最新状态
Android 工具几乎在每一次更新中都会获得构建优化和新功能,本页介绍的一些提示假设您正在使用最新版本。要充分利用最新优化,请保持以下工具处于最新状态:
为开发创建构建变体
准备发布应用时需要的许多配置在开发应用时都不需要。启用不必要的构建进程会减慢您的增量构建和干净构建速度,因此,在开发您的应用期间,配置一个构建变体,使之仅包含您需要的构建配置。下面的示例将为您的发布版本配置创建一个“开发”风味和一个“生产”风味:
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. } } }
如果您的构建配置已使用产品风味创建不同版本的应用,您可以使用风味维度,将“开发”和“生产”配置与这些风味组合。例如,如果您已配置“演示”和“完整”风味,您可以使用下面的示例配置创建组合风味,例如“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 { ... productFlavors { dev { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resConfigs "en", "xxhdpi" } ... } }
为您的调试构建停用 Crashlytics
如果您不需要运行 Crashlytics 报告,请按以下步骤操作来停用插件,以便加快您的调试构建的速度:
android { ... buildTypes { debug { ext.enableCrashlytics = false } }
您也需要更改在应用中为 Fabric 初始化支持的方式,在运行时为调试构建停用 Crashlytics 套件,如下所示:
// 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);
如果您想要将 Crashlytics 与调试构建结合使用,仍可以通过以下方式来加快增量构建的速度:在每个构建期间阻止 Crashlytics 使用它自己的独特构建 ID 更新应用资源。要阻止 Crashlytics 不断更新其构建 ID,请将以下代码添加到您的 build.gradle
文件中:
android { ... buildTypes { debug { ext.alwaysUpdateBuildId = false } }
如需了解有关在使用 Crashlytics 时优化您的构建的详细信息,请阅读官方文档。
将静态构建配置值与调试构建结合使用
始终为进入 manifest 文件的属性使用静态/硬编码值,或者为您的调试构建类型使用资源文件。如果您的 manifest 文件或应用资源中的值需要随着每一个构建更新,Instant Run 将无法执行代码交换 - 它必须构建和安装新的 APK。
例如,在您每次想要运行更改时,使用动态版本代码、版本名称、资源或任何其他可以更改 manifest 文件的构建逻辑都需要一个完整的 APK 构建 - 即使实际更改仅需要一个热交换,也是如此。如果您的构建配置需要此类动态属性,那么将其隔离到您的发布构建变体中并让值对您的调试构建保持静态,如下面的 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 (which is not supported by Instant Run). 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,请执行以下操作:
- 点击 File > Settings(在 Mac 上,点击 Android Studio > Preferences),打开 Preferences 窗口。
- 在左侧窗格中,点击 Build, Execution, Deployment > Gradle。
- 勾选 Offline work 复选框。
- 点击 Apply 或 OK。
如果您正在从命令行构建,请传递 --offline
选项。
启用按需配置
为了让 Gradle 准确了解如何构建您的应用,构建系统会在每个构建前在项目中配置所有模块以及这些模块的依赖项(即使您正在构建和测试一个模块,也是如此)。这会减慢大型多模块项目的构建进程。要指示 Gradle 仅配置您想要构建的模块,请按以下步骤操作,启用按需配置:
- 点击 File > Settings(在 Mac 上,点击 Android Studio > Preferences),打开 Preferences 窗口。
- 在左侧窗格中,点击 Build, Execution, Deployment > Compiler。
- 勾选 Configure on demand 复选框。
- 点击 Apply 或 OK。
创建库模块
在应用中查找您可以转换成 Android 库模块的代码。通过这种方式将您的代码模块化可以让构建系统仅编译您修改的模块,并缓存这些输出以用于未来构建。这种方式也会让按需配置和并行项目执行更有效(如果您启用这些功能)。
为自定义构建逻辑创建任务
在您创建构建分析后,如果分析显示构建时间中相当大的一部分用在了“配置项目”阶段,请检查 build.gradle
脚本并查找您可以添加到自定义 Gradle 任务中的代码。将某个构建逻辑移动到任务中后,它仅会在需要时运行,可以为后续构建缓存结果,并且该构建逻辑将有资格并行运行(如果您启用并行项目执行)。要了解详情,请阅读官方 Gradle 文档。
提示:如果您的构建包含大量自定义任务,您可能还需要通过创建自定义任务类来整理 build.gradle
文件。将您的类添加到 project-root/buildSrc/src/main/groovy/
目录中,Gradle 会自动将其添加到项目中所有 build.gradle
文件的类路径中。
配置 dexOptions 和启用库预 dexing
Android 插件提供了 dexOptions
代码块,因此,您可以配置以下 DEX 构建属性,这样可能会加快构建速度:
preDexLibraries
:声明是否预 dex 库依赖项以加快您的增量构建速度。由于此功能可能减慢您的干净构建的速度,您可能需要为持续性集成服务器停用此功能。maxProcessCount
:设置运行 dex-in-process 时要使用的最大线程数量。默认值为 4。javaMaxHeapSize
:设置 DEX 编译器的最大堆大小。不过,您应当增加 Gradle 的堆大小(启用 dex-in-process 时,将与 DEX 编译器共享),而不是设置此属性。
例如:
android { ... dexOptions { preDexLibraries true maxProcessCount 8 // Instead of setting the heap size for the DEX process, increase Gradle's // heap size to enable dex-in-process. To learm more, read the next section. // javaMaxHeapSize "2048m" } }
您应当递增这些设置的值来试验它们并通过分析您的构建观察效果。如果您向进程分配过多的资源,性能可能会下降。
注:如果 Gradle 后台进程已在运行,您需要先停止此进程,然后使用新设置进行初始化。您可以选择 View > Tool Windows > Terminal 并输入 gradlew --stop
命令来终止 Gradle 后台程序。
增加 Gradle 的堆大小并启用 dex-in-process
Dex-in-process 在构建进程内而不是单独的 VM 进程中运行 DEX 编译器 - 这会同时加快增量构建和干净构建的速度。默认情况下,使用 Android Studio 2.1 及更高版本创建的新项目将向构建进程分配足够的内存来启用此功能。如果您并非使用 Android Studio 2.1 或更高项目创建项目,则需要将 Gradle 后台程序的最大堆大小至少设置为 1536 MB。以下示例会在项目的 gradle.properties
文件中将 Gradle 的堆大小设置为 2048 MB:
org.gradle.jvmargs = -Xmx2048m
一些较大的项目可能会从更大的 Gradle 堆大小中受益。不过,如果您正在使用内存较小的机器,可能需要配置 IDE,使其使用较少的内存。要了解您分配给 IDE 和 Gradle 的资源量如何影响构建性能,请转到有关分析您的构建的部分。
注:如果 Gradle 后台进程已在运行,您需要先停止此进程,然后使用新设置进行初始化。您可以选择 View > Tool Windows > Terminal 并输入 gradlew --stop
命令来终止 Gradle 后台程序。
如果您在模块的 build.gradle
文件(控制着 DEX 编译器的堆大小)中为 android.dexOptions.javaMaxHeapSize
定义值,则需要将 Gradle 的堆大小设置为比您为 javaMaxHeapSize
属性设置的值大 512 MB 并且至少为 1536 MB。例如,如果您将 javaMaxHeapSize
设为 1280 MB,则必须将 org.gradle.jvmargs
至少设置为 1792 MB (1280 + 512),不过,堆大小越大越好。您不需要为 javaMaxHeapSize
指定值来启用 dex-in-process。如果您将 javaMaxHeapSize
从构建配置中排除,只需确保将 Gradle 的堆大小设置为 1536 MB 或更高。
将图像转换成 WebP
WebP 是一种既可以提供有损压缩(像 JPEG 一样)也可以提供透明度(像 PNG 一样)的图片文件格式,不过与 JPEG 或 PNG 相比,这种格式可以提供更好的压缩。降低图片文件大小可以加快构建的速度(无需执行构建时压缩),尤其是在您的应用使用大量图像资源时,更是如此。不过,在对 WebP 图像进行解压缩时,您可能会注意到设备的 CPU 使用率有小幅上升。使用 Android Studio 时,您可以轻松地将图像转换成 WebP。
停用 PNG 处理
如果您无法(或者不想)将 PNG 图像转换成 WebP,仍可以通过在每次构建应用时停用自动图像压缩的方式加快构建速度。要停用此优化,请将以下代码添加到您的 build.gradle 文件中:
android { ... aaptOptions { cruncherEnabled false } }
由于构建类型或产品风味不定义此属性,在构建发布版本的应用时,您需要将此属性手动设置为 true
。
启用 Instant Run
Instant Run 可以在不构建新 APK 的情况下(某些情况下,甚至不需要重启当前 Activity)推送特定代码和资源更改,从而显著缩短更新您的应用所需的时间。在对您的应用进行更改后,点击 Apply Changes 以使用 Instant Run,此功能会在您执行以下操作时默认启用:
- 使用调试构建变体构建您的应用。
- 使用 Android Plugin for Gradle 2.3.0 或更高版本。
- 在应用的模块级
build.gradle
文件中将minSdkVersion
设置为 15 或更高。 - 点击 Run ,将您的应用部署到运行 Android 5.0(API 级别 21)及更高版本的设备上。
如果满足这些要求后您在工具栏中没有看到 Apply Changes 按钮,请按以下步骤操作,确保未在 IDE 设置中停用 Instant Run:
- 打开 Settings 或者 Preferences 对话框。
- 导航至 Build, Execution, Deployment > Instant Run。
- 确保勾选 Enable Instant Run。
启用构建缓存
构建缓存可以存储 Android Plugin for Gradle 在构建您的项目时生成的特定输出(例如未打包的 AAR 和 pre-dexed 远程依赖项)。使用缓存时,您的干净构建将显著加快,因为构建系统在后续构建期间可以直接重用这些缓存文件,而不用重新创建它们。
使用 Android 插件 2.3.0 及更高版本的新项目在默认情况下会启用构建缓存(除非您明确停用构建缓存)。要了解详情,请阅读使用构建缓存加快干净构建的速度。
停用注解处理器
使用注解处理器时,增量 Java 编译处于停用状态。如果可以,请尝试避免使用注解处理器,以便在不同构建之间仅编译您修改的类。
分析您的构建
较大的项目或者实现许多自定义构建逻辑的项目可能要求您深入查看构建进程才能找到瓶颈。为此,您可以分析 Gradle 执行构建生命周期的每个阶段和每个构建任务所需的时间。例如,如果您的构建分析显示 Gradle 在配置项目时花费了过多的时间,可能表明您需要将自定义构建逻辑移出配置阶段。此外,如果 mergeDevDebugResources
任务占用了大量构建时间,则表明您还需要将图像转换成 WebP 或者停用 PNG 处理。
使用分析来加快构建速度一般涉及在启用分析的情况下运行构建,对构建配置进行一些调整,以及进行更多分析来观察更改的结果。
要生成和查看构建分析,请执行以下步骤:
- 在 Android Studio 中打开您的项目后,选择 View > Tool Windows > Terminal 以在项目的根目录下打开命令行。
- 输入以下命令来执行干净构建。在分析构建时,您应当在分析的每个构建之间执行干净构建,因为 Gradle 会在任务(例如源代码)的输入未发生变化时跳过任务。因此,没有输入更改的第二个构建总会以更快的速度运行,因为任务不会重复运行。在不同构建之间运行
clean
任务可以确保您分析完整的构建进程。// On Mac or Linux, run the Gradle wrapper using "./gradlew". gradlew clean
- 使用以下标志为您的产品风味之一(例如“开发”风味)执行调试构建:
gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebug
--profile
:启用分析。--recompile-scripts
:在绕过缓存时强制重新编译脚本。--offline
:禁止 Gradle 提取在线依赖项。这样可以确保 Gradle 在尝试更新依赖项时引起的任何延迟都不会干扰您的分析数据。您应当已将项目构建一次,以便确保 Gradle 已经下载和缓存您的依赖项。--rerun-tasks
:强制 Gradle 重新运行所有任务并忽略任何任务优化。
- 在构建完成后,请使用 Project 窗口导航至
project-root/build/reports/profile/
目录(如图 1 所示)。 右键点击
profile-timestamp.html
文件,然后选择 Open in Browser > Default。报告应与图 2 中显示的类似。您可以检查报告中的每个标签来了解构建,例如,Task Execution 标签显示了 Gradle 执行每个构建任务花费的时间。可选:在对您的项目或构建配置进行任何更改之前,请重复第 3 步中的命令,但请忽略
--rerun-tasks
标志。由于 Gradle 会尝试通过不重复执行输入未发生变化的任务来节省时间(这些任务在报告的 Task Execution 标签中标记为UP-TO-DATE
,如图 3 中所示),您可以确定哪些任务在不必要的时间执行了工作。例如,如果:app:processDevUniversalDebugManifest
未标记为UP-TO-DATE
,则可能说明您的构建配置会随着每个构建动态更新 manifest。不过,有些任务(例如:app:checkDevDebugManifest
)需要在每个构建期间始终运行。
现在,您已经有了一个构建分析报告,可以通过检查报告每个标签中的信息来寻找优化机会。有些构建设置要求实验,因为它们的好处在项目与工作站之间可能有所不同。例如,包含大型代码库的项目可能会受益于使用 ProGuard 移除未使用的代码和压缩 APK 大小。不过,较小的项目则可能从完全停用 ProGuard 中受益。此外,增加 Gradle 堆大小可能会降低小内存机器的性能。
提示:如需更强大的分析工具,请考虑使用 Gradle 的开放源代码分析器https://github.com/gradle/gradle-profiler