如何提高Android Studio构建速度

Tips:
自从切换到AS开发安卓应用以来,一直被AS蜗牛般的构建速度困扰着,特别是在构建大型项目的时候如果构建变种很多的话,速度更是慢的够呛,所以最近研究一下如何提高编译速度,在网上找到这样一篇文章,供大家参考。

文章来源:

  1. 优化您的构建速度 | Android Developers
  2. 几个小改动,将Android 构建速度提升10倍以上!

下面提取文章重点,以做备忘。
在我们谈论优化措施之前,我们先来看看优化之前项目的build性能

1
2
gradlew clean //执行干净构建
gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebug

  • profile:启用分析。
  • recompile-scripts:在绕过缓存时强制重新编译脚本。
  • offline:禁止 Gradle 提取在线依赖项。这样可以确保 Gradle 在尝试更新依赖项时引起的任何延迟都不会干扰您的分析数据。您应当已将项目构建一次,以便确保 Gradle 已经下载和缓存您的依赖项。
  • rerun-tasks:强制 Gradle 重新运行所有任务并忽略任何任务优化。
    我们使用这个命令执行full-build(从头创建)的过程,来衡量build的性能。

    优化您的构建配置

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

    保持工具处于最新状态

    Android 工具几乎在每一次更新中都会获得构建优化和新功能,本页介绍的一些提示假设您正在使用最新版本。要充分利用最新优化,请保持以下工具处于最新状态.

    避免Legacy Multidex

    Google针对64k方法数的问题推出了MultiDex的解决方法,但是不同的api版本上,multidex的做法是不一样的,在api21以上,因为Android采用了新的运行时ART,会在安装的时候将所有的classesN.dex合并成一个.oat包。你需要做的就是在编译脚本中加一行 multiDexEnabled true。但是在api以下,你需要引入multidex support library. 而且在编译过程中,编译脚本要话很长时间决定哪些class要放入primary dex中,哪些放入secondary dex中。在api21以下,这叫做legacy multidex。

开发中,我们可以避免legacy multidex带来的编译性能消耗.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
productFlavors{

dev {
minSdkVersion 21
applicationIdSuffix '.dev'

applicationId "com.daren.dbuild_province"
versionName "2.7.0"
versionCode 20180619

resConfigs ("cn","xxhdpi")
dimension "default"

}
}

避免编译不必要的资源

这样我们在开发环境下只引入英文资源和xxhdpi下的资源,减小打包时间.

1
2
3
4
5
6
7
8
9
10
11
12
android {
...
productFlavors {
dev {
...
// The following configuration limits the "dev" flavor to using
// English stringresources and xxhdpi screen-density resources.
resConfigs "en", "xxhdpi"
}
...
}
}

为您的调试构建停用 Crashlytics(用的比较少)

如果您不需要运行 Crashlytics 报告,请按以下步骤操作来停用插件,以便加快您的调试构建的速度.

1
2
3
4
5
6
7
android {
...
buildTypes {
debug {
ext.enableCrashlytics = false
}
}

如果您想要将 Crashlytics 与调试构建结合使用,仍可以通过以下方式来加快增量构建的速度:在每个构建期间阻止 Crashlytics 使用它自己的独特构建 ID 更新应用资源。要阻止 Crashlytics 不断更新其构建 ID,请将以下代码添加到您的 build.gradle 文件中:

1
2
3
4
5
6
7
android {
...
buildTypes {
debug {
ext.alwaysUpdateBuildId = false
}
}

对进入AndroidManifest.xml 的配置进行静态构建配置值与调试构建结合使用

为进入AndroidManifest.xml 的配置值考虑进行动态配置和静态配置,因为如果您的 manifest 文件或应用资源中的值需要随着每一个构建更新,Instant Run 将无法执行代码交换 - 它必须构建和安装新的 APK。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 检查有无更新而减慢构建速度。您应改为使用静态/硬编码版本号。
例如:

1
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.7.0'

而不要用

1
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.7.+'

启用离线模式

如果你的网络环境不好,链接速度比较慢,那么在Gradle尝试使用网络下载依赖包的时候,构建速度会变慢,所以如果您的依赖包都已经被缓存之后,可以打开“Offline work”模式。

增加 Gradle 的堆大小并启用

修改gradle.properties文件,将 Gradle 的堆大小设置为 2048 MB

1
org.gradle.jvmargs=-Xmx4608m -XX:MaxPermSize=1024m

停用 PNG 处理

1
2
3
4
5
6
7
android {
...
aaptOptions {
cruncherEnabled false
}
}
由于在productFlover和buildtype中不定义此属性,在构建正式版本的应用时,您需要将此属性手动设置为 true。

禁用动态的BuildConfig(开发环境)

1
buildConfigField "long", "BUILD_TIMESTAMP", getTimeStamp() + "L"

我们对BuildConfig中的一项输入做了动态变化,getTimeStamp会获取当前的时间,导致每次build时这个值都会发生变化。当然在release环境下,这个值确实有用,但是在开发中,就没必要了。它会导致重新生成BuildConfig.java, 继而导致 javac, dex, package,sign等一系列的任务重新执行,需要对其优化

1
2
3
buildConfigField "long", "BUILD_TIMESTAMP",

  project.hasProperty("devBuild")?"000000000":getTimeStamp() + "L"

这样如果是开发环境,每次这个值都是一样的。再来看看优化之后的profile

分享到