当我们在开发一个Android项目的时候,随着功能的增加,代码的体量也越来越大,从一开始的十几个文件,到后面动辄几千行代码的类文件,十几个第三方库的嵌入,构建的时间也从一开始的十几秒,到后面的十几分钟,严重的影响了我们的开发效率,于是下决心好好研究一下如何提高Gradle的构建效率,下面是我总结的几点:
保持你的开发环境更新
保持你的构建工具版本最新,比如Android Gradle3.5重中之重就是提高项目的构建速度。所以保证你的构建工具最新,能在一定程度上提升你的构建效率。当然一般情况下,我们为了保证开发环境的稳定,一般不会去轻易更新开发工具和构建工具,有可能会带来意想不到的问题。但是新版本的优化,值得你为此抽出一些时间来更新。所以想要充分利用最新的优化,请确保以下工具已是最新版本:
创建用于开发的构建变体
我们在平时开发过程中,很多应用发布时的配置时不必须的,启用这些配置,会在一定程度上影响我们的构建速度,我们可以建立一个构建变体,将一些日常开发过程中并不需要的构建配置去除,比如混淆应用,下面为你展示如何配置一个开发变体:
android {
...
defaultConfig {...}
buildTypes {...}
//变体配置
productFlavors {
//当指定构建变体后,这里的配置将覆盖defaultConfig中的配置
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//为了避免使用旧版的multidex,这里可以吧minSdkVersion指定到21以上,当你使用的Android Studio在2.3以上进行部署时,构建工具会自动避免使用旧版的multidex,这个参数对此就没有影响了。
versionNameSuffix "-dev"
applicationIdSuffix '.dev'
}
prod {
//如果你已经在defaultConfg模块中配好了发布版本的配置,那么这里空着就可以了,但是一定要有,
//不然所有的版本都将安装“dev”的变体配置进行构建
}
}
}
配置同步,当我们修改build.gradle文件的时候,需要让项目与构建配置同步,这一步是比较耗时的,如果你有多个构建变体的话,那每改一点都需要同步很久,如果你使用了Android Studio3.3以上的版本和Android Gradle插件3.3以上,就可以让同步仅限于当前所选的变体,来优化同步耗时,一般来说,这个优化都是默认启动的,如果你需要修改他,可以依次点击 File > Settings > Experimental > Gradle(在 Mac 上,则依次点击 Android Studio > Preferences > Experimental > Gradle),然后勾选 Only sync the active variant 复选框即可。
注意:此优化完全支持包含 Java 和 C++ 语言的项目,部分支持包含 Kotlin 语言的项目。在为包含 Kotlin 内容的项目启用此优化时,Gradle 同步会回退到在内部使用完整的变体。
避免编译不需要的资源
当我们在开发过程中,我们并不需要那么多的诸如语言本地化或者屏幕适配相关的资源文件,我们可以在我们的开发分支中指定一个语言资源和屏幕密度,当然如果你的项目只在某个国家中发行(比如中国),你也可以直接在defaultConfig中直接指定语言资源,这样也能从一定程度上优化安装包的大小:
android {
defaultConfig {
resConfigs "zh-rCN"
}
...
productFlavors {
dev {
...
// 以下配置将为dev指定简体中文字符资源和xxhdpi屏幕密度资源
resConfigs "zh-rCN", "xxhdpi"
}
...
}
}
对调试 Build 停用 Crashlytics
如果您不需要运行 Crashlytics 报告,请按如下方法停用该插件,以提高调试 Build 的构建速度:
android {
...
buildTypes {
debug {
ext.enableCrashlytics = false
}
}
如果你在程序中使用来Fabric,还需要将程序初始化对Fabric对支持方式做更改,在运行是对调是Build停用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在每次构建过程中使用自己但唯一Build ID更新应用资源,提高增量构建的速度。
android {
...
buildTypes {
debug {
ext.alwaysUpdateBuildId = false
}
}
将静态构建配置值用于调试Build
如果你在运行时需要用到一些动态的值,比如动态的版本代码,版本名称,资源或者可以修改清单文件的任何其他构建逻辑,都需要完整的APK Build。即使十几修改仅需要一个热交换,也是如此,所以如果你的构建配置中有这类的动态属性,可以将它隔离到你的发布版本构建变体中,并使该值对你的调试Build保持静态,比如下面这个文件所示。
int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE
android {
...
defaultConfig {
//在defaultConfig中动态设置这两个值中的任何一个都需要完整的APK构建和重新安装,因为必须更新AndroidManifest.xml。
versionCode 1
versionName "1.0"
...
}
// 上面的defaultConfig值是固定的,因此增量构建不需要重新构建清单(因此也不需要重新构建整个APK,从而降低构建时间)。
//但对于发布版本来说,没关系。
//因此,下面的脚本遍历所有已知的变量,找到那些“release”构建类型,并将这些属性更改为动态的。
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 选项。
创建库模块
对于我们一些不常需要修改的代码,比如一些工具类,或者是底层框架性的代码,我们可以将他们从项目中抽出来单独形成一个库模块,这样模块化项目,构建系统只会编译你修改的模块,这样能在一定程度上优化构建效率,如果你们有自己的Maven私服,比如nexus,还可以将库模块从项目里面分离出来上传到私服像引用第三方库一样引用,这样对于项目在底层代码更新上也会有一定的好处,假如你们的项目很多的话。如何将库模块封装并上传到我们自己的私服上,我们在后面的文章再做介绍。
并行项目执行
但我们在分离代码模块后没我们可以让我们的项目并行构建,最大程度的利用我们编译机器的性能。如果你是使用命令进行构建项目的,可以使用--parallel
命令行参数,或者配置你的Gradle构建环境,在你的gradle.properties文件中配置org.gradle.parallel=true
即可。
为自定义的构建逻辑创建任务
在我们在创建构建分析报告之后,如果分析报告显示配置项目占用来大部分的时间,可以检查我们的build.gradle脚本并查找是否有可以将其添加到自定义Gradle任务中的代码,将这些构建逻辑一道任务中,它们将会在需要时才会运行,并且缓存运行结果用于构建,并且这个构建逻辑也可以并行运行,如果你启用来并行构建的话。
如果您的版本中包含大量自定义任务,则您可能需要通过创建自定义任务类来整理
build.gradle
文件。将您的类添加到project-root</var>/buildSrc/src/main/groovy/
目录中,Gradle 会自动将其添加到项目中所有build.gradle
文件的类路径中。
压缩图片大小
降低图片文件的大小可以在一定程度上加快构建效率,WebP是一种不过的选择,这种格式提供来更好的压缩效果,但是相对带来的影响,就是你的程序在运行是解压WebP会比JPEG,PNG格式相对耗性能一些,如果你的应用使用到来大量的图片资源,你可能要权衡一些压缩图片对应用运行时的流畅度带来的影响了。
如果你不想将图片转换称WebP格式,你也可以在调试时通过停止构建时的图片自动压缩来提高构建效率。如果你使用的是Android插件3.0.0以上的版本,默认情况下针对debug版本构建类型停用PNG处理,如果你还想对其他构建类型停用此优化,可以将下面代码添加到build.gradle文件中:
android {
buildTypes {
release {
// 针对release构建类型停止图片压缩
crunchPngs false
}
}
// 如果你使用的旧版插件,可以用以下配置:
// aaptOptions {
// cruncherEnabled false
// }
}
因为构建类型和产品变种不定义这个属性,所以在构建应用的发布版本的时候,你需要手动将这个属性设置为true。
启用构建缓存
构建缓存可以存储构建项目是Android Plugin for Gradle 生成的特定输出(例如,未打包的AAR和进过dex预处理的远程依赖项)。使用缓存,在初始化构建后,构建速度会明显加快。因为构建系统会在后续构建时直接重用这些缓存文件。
使用Android 插件2.3.0以上的版本项目,默认会启用构建缓存。除非你自己手动去停用它。
使用增量注解处理器
Android Gradle插件3.3.0以上的版本改进来对增量注解处理的支持,因此如果想要提高构建速度,你还需要更新你的Android Gradle插件,并且尽可能仅使用增量注解处理器。
增加内存
当你发现你已经尽量优化构建效率,发现效率还是没有提升多少,那可以试试增大你的jvm内存,可以在gradle.properties中添加配置org.gradle.jvmargs=-Xmx512m
最小化仓库数量
当Gradle尝试解决依赖关系时,它将按照声明它们的顺序搜索每个存储库,直到找到该依赖关系为止。通常,这意味着您要先声明存储最大数量依赖关系的存储库,以便在大多数情况下仅搜索该存储库。您还应将已声明的存储库数量限制为最小可行数量,以使构建正常工作。
如果您使用的是自定义存储库服务器,则可用的一种技术是创建将多个实际存储库聚集在一起的虚拟存储库。然后,您可以仅将该存储库添加到构建文件中,从而进一步减少Gradle在依赖关系解析期间发送的HTTP请求的数量。
减少不必要和未用到的依赖项
每增加一个依赖项,就意味着增加不少代码量,增大项目体量的同时,构建效率上也会又一定的影响,所以及时清理不需要的依赖项,也是提高构建效率的方法。
构建性能剖析
如果你已经实行了以上的各种提高构建效率的方法,但对与构建效率还是不满意,你可以构建分析报告来分析Gradle在什么地方花费的时间过多,来优化构建效率。生成和查看构建分析报告可以查看这里
最后,优化构建效率只是最终的目的是为了提高我们的开发效率,除了一台好的开发机,简化我们的构建流程,保证你的开发环境不会太过陈旧,保持良好的编码习惯也是提升效率的方式之一,毕竟bug少了,调试的次数自然也减少了。后续我们将尝试如何优化编译后的apk体积。
参考文档:
https://developer.android.google.cn/studio/build/optimize-your-build
https://developer.android.google.cn/studio/releases/gradle-plugin
http://google.github.io/android-gradle-dsl/current/
https://guides.gradle.org/performance/