6个技巧加速你的gradle编译

简介: 最近我们都在讨论build系统,我们看了一些技巧可以让你的Maven build更快。结论和反映都势不可挡。由于我们提供的技巧,更多的人都很高兴能加快他们完成自己的项目。

最近我们都在讨论build系统,我们看了一些技巧可以让你的Maven build更快。结论和反映都势不可挡。由于我们提供的技巧,更多的人都很高兴能加快他们完成自己的项目。现在,让我们看一下怎么处理gradle编译项目。编译的项目一般都是标准编译的,也都是独一无二的。几乎所有的项目都增加了其自身的复杂性。所有的东西都不同但是有一个东西是相同的:编译会占用你的时间,加快编译会影响你的开发效率,让你的项目工作更加顺畅。

事不宜迟,让我们来看看什么是Gradle,和它的理念:
这里写图片描述

加速Gradle编译

这篇文章主要是由Madis Pink大力提倡的:Squeezing the Last Drop of Performance Out of Your Gradle Builds.Madis是JRebel的Android工程师,所以如果你是一个搞Android的,我建议你应该试一下。Madis热衷于这些,但是你不会观察到有关他太多。

对于一个测试项目我们用Madis用过的代码:一个Android项目demo iosched(http://github.com/google/iosched)。不要害怕,gradle对于Android项目和你的Java项目是一样的。这意味着我给你的建议同样适用于你其他的项目的环境。所以你同样也能用这些技巧去加速你的JAVA项目编译。

在开始优化之前,我们首先需要理解一下Gradle的生命周期,它被拆分为3个不同阶段:

  1. 初始化:扫描项目,找出哪些内容需要被编译
  2. 配置:运行build.gradle脚本,创建任务图
  3. 执行:构建你APP有用的部分

现在你是不是头痛了?确实有一个有用的阶段,我们也许能够在我们自己的编译脚本加快,Gradle完全执行自私的任务:配置本身和实施执行开销。 在这篇文章中,我们将首先集中精力减少构建的开销之前,我们尽量使构建本身更快。

让我们开始一步优化构建步骤,同时测量进度。如果你想自己运行 iosched,从GitHub得到它,就像这样:

git clone http://github.com/google/iosched
cd ioshed

现在我们准备去克隆了!让我们用手中典型的开发环境用gradle去build这个APP来获取依赖。
再次编译我们的项目,但是用dry-run(能够让gradle去跳过所有任务的执行)。
这意味着,我们将执行配置gradle,并执行所有它通常会做的任务只是没有做实际工作。这正是我们需要测试并且减少开销的。
执行以下命令几次,因为你第一次做这样的构建将拉低所需的依赖,如果你使用一个新的项目。执行下面的命令:

./gradlew :android:assembleDebug --dry-run

在考虑到所有的gradle执行的任务之后,跳过dry-run,会打印出运行这个命令会消耗多少时间。在我的2013年的MacBook Pros上仍然需要9s。

BUILD SUCCESSFUL
Total time: 8.674 secs

一个标准的测量时需要多次运行命令,然后去出平均测量结果。因为我们不是在做科学实验,所有跳过这些鼓噪的步骤。带着一粒盐,你的里程可能会发生变化(这句话我也没懂什么意思…)

第二步是在gradle构建时启用分析,去看这些gradle命令你会获取到一份好的日志:

./gradlew :android:assembleDebug --dry-run --profile
 open build/reports/profile/profile-2016-02-02-15-39-17.html

这份日志显示,大部分的时间消耗在了配置项目上:
这里写图片描述
所以让配置更快一些。

1.使用配置需求

有一个减少时间的方法:我们需要尽早的让gradle去配置,幸运的是,这只是另外的一种添加命令标志:

./gradlew :android:assembleDebug --dry-run --profile --configure-on-demand

很明显,结果好了一些,变成了7s:

BUILD SUCCESSFUL
Total time: 7.144 secs
这个日志显示了在我们调用了这个命令之后减少了2.359s。似乎可以忽略不计,但是换句话来说你就会觉得有意义了–这是一个17%的加速了。

配置这样一个命令对gradle是一个孵化的功能,所以它不是默认启用的。或许将来的一天可以默认开启,但是现在我们可以全局使用它,通过在你的home目录下加一行.gradle/gradle.properties,
这个命令也满足在linux和OSX系统下:

echo 'org.gradle.configureondemand=true' >> ~/.gradle/gradle.properties

2.使用gradle daemon

现在,因为我们正在谈论全局性,我们也可以使用gradle daemon。gradle daemon是一个后台进程,在gradle构建完成之前不会退出。下次你可以直接调用gradle,它仍然等待你下次调用。这有很大意义,因为gradle是一个需要启动的JVM进程,加载JVM,加载class,JIT等等。gradle daemon的作用就是限制所有的开销。
让我们比较一下使用gradle daemon和不适用gradle daemon所消耗的时间:

./gradlew :android:assembleDebug --dry-run --no-daemon
# vs. 
./gradlew :android:assembleDebug --dry-run --daemon

在我的机器上,一段时间后,使用gradle daemon要比不适用快的不是一点点:

BUILD SUCCESSFUL
Total time: 2.536 secs
现在仅仅用了2.536s,是不是很爽?用gradle daemon确实很棒,所以你应该把它设置成全局的。

echo 'org.gradle.daemon=true' >> ~/.gradle/gradle.properties

3.用最新的gradle版本

下面我们开始讨论一下gradle的版本问题。gradle是一个比较复杂的‘怪物’,大多数的项目随着每个release版本越来越快,所以用最新的版本有很大意义。
到现在为止最新的gradle版本是2.2.1,最新的gradle release更新是2.10,,让我们升级用最新的版本。用不同的构建工具,升级的过程是很痛苦的。gradle不一样,大多数项目都用的gradle编译,修复gradle版本确保构建重复性。如果你的项目用gradle编译确实很棒,并且你也应该用wrapper。例如在他的 Virtual JUG session上面,Andres Almiray:JAVA大牛,也是一个gradle粉丝。相信他,他对gradle的了解不是一点点。

当我们用wrapper的时候,去改变我们正在用的gradle版本,仅仅需要去更改wrapper配置中的几个数字而已。这个配置文件在 项目根目录gradle/wrapper/gradle-wrapper.properties文件下面。
遗憾的是,由于配置上的一些bug,gradle很容易失败:

Failed to apply plugin [id ‘com.android.application’]
Gradle version 2.2 is required. Current version is 2.10. If using the gradle wrapper, try editing the distributionUrl in /Users/shelajev/repo/tmp/iosched/gradle/wrapper/gradle-wrapper.properties to gradle-2.2-all.zip

很显然,这是一个在比较gradle版本的时候出现的问题,如果用gradle wrapper,试着去改成2.9.然后我们看一下2.9的编译速度:

BUILD SUCCESSFUL
Total time: 1.356 secs
gradle 2.9 果真没让我们失望,从来没这么快过,由之前的8s编程现在的1.3s。

同理也使用与JAVA版本。如果你还没有升级到JAVA1.8,马上升级吧。读完这篇文章,马上行动吧。你还没有用JAVA 8的lambdas.
确保你的构建工具最新,那么你会得到最高效的JAVA版本执行。

4.优化项目

到现在为止,我们一直在谈编译消耗在构建上。说实话,大部分你能够加速优化的地方在实际的构建过程中隐藏掉了。好吧,在我们的demo中,我们保存的大部分时间在消除开销,但是我们看看生成项目会发生什么?让我们看一下怎样能真正的加速gradle构建。

5.避免繁重的计算

通常情况下,我们可以避免大部分的gradle构建所做的繁重的工作。让我们看看demo,尝试去减少gradle构建时的IO输出。例如,你现在构建一个典型的APP为了持续集成,你需要去保存你构建的的一些信息。

这些信息仅仅是一些命令?在你的gradle.build文件中你可以看到:

def cmd = 'git rev-list HEAD --first-parent --count'
def gitVersion = cmd.execute().text.trim().toInteger()
android {
  defaultConfig {
    versionCode gitVersion
  }
}

上面的代码执行一个Git命令,并将结果以供以后使用变量。但是实际上,命令执行需要很多时间。为了您的开发环境的目的,你可能并不需要这些信息。幸运的是:gradle真的很灵活,这些配置只是纯的Groovy文件。所以,如果你改变上面的配置,就像下面的例子一样:

def gitVersion() {
  if (!System.getenv('CI_BUILD')) {
    // don't care
    return 1
  }
  def cmd = 'git rev-list HEAD --first-parent --count'
  cmd.execute().text.trim().toInteger()
}
android {
  defaultConfig {
    versionCode gitVersion()
  }
}

很轻松避免这样的计算,所以你应该注意将上面的代码保存下来。

6.修复依赖

gradle允许你指定项目中依赖包的范围,在下面的例子中,任何一个gson 2的小版本都满足依赖约束。事实上,gradle尝试去找最新的版本,这就消耗了gradle的灵活性。gradle不得不去网上查询哪个版本可用。这有的是不必要的,尤其如果你的网络环境很差,像这样:

dependencies {
    compile 'com.google.code.gson:gson:2.+'
}

这样不仅会减慢你的项目编译,同时也会失去了重复性的构建。
在任何的情况下,避免动态依赖和固定版本号都是一个好方法,这样做不难,只需要找到gradle现在的版本号并且指定这个数就OK了。

模块化项目和并行化编译

最后,这个并不是特别重要,但是也许是最有影响力的,它能提高你的项目编译速度并且使你的项目模块快更好。首先,模块化项目可以并行编译。我们谈论了它如何加快Maven和gradle。并行编译,这是另一种孵化功能,您需要提供另一个命令行标志。你也可以给你的gradle命令或者gradle.properties文件中加一个全局的flag

echo 'org.gradle.parallel=true' >> ~/.gradle/gradle.properties

除了明显的加速,它也比多线程构建多了以下几个好处:
1. 并行工程的配置
2. 复用之前的项目
3. 项目得到及时检查
4. 在项目编译过程中使用了预编译
最后两点比较重要,它能够及时的很好的改变你的代码。这意味着gradle可以弄清楚并且能够避免不必要的构建项目。这所做的工作是有史以来最快的工作。

结论

在Madis Pink的讨论中我们看到了几个好的建议。以下是简短几点:

  • 启用按需配置
  • 用gradle daemon
  • 及时更新新版本
  • 避免做繁重的计算
  • 不要动态使用依赖
  • 并行编译

其中的一些建议能够减少gradle本身的配置,减少你的项目构建,以及其他类似避免动态依赖和并行的执行。这些将使你的项目构建节省很多时间。更加让我们开心的是,这些建议同样使用与JAVA项目的构建。
如果你有其他的方法来更快的构建gradle,我更加开心。记得把那些建议发给我,我会尽我所能来开源这些好的知识。当然你也可以从这篇文章延伸出自己的更好的方法。到时候记得在Twitter上@我,咱俩可以聊聊。

转自:
http://zeroturnaround.com/rebellabs/making-gradle-builds-faster/.

翻译自:
https://medium.com/@shelajev/6-tips-to-speed-up-your-gradle-build-3d98791d3df9#.2wvd1b2i3

相关文章
|
Android开发
IDEA编译gradle提示This version of the Android Support plugin for IntelliJ IDEA (or Android Studio) cannot open this project, please retry with version 2020.3.1 or newer.
IDEA编译gradle提示This version of the Android Support plugin for IntelliJ IDEA (or Android Studio) cannot open this project, please retry with version 2020.3.1 or newer.
839 1
|
1月前
|
Android开发
解决Android、Flutter编译时Gradle报错:javax.net.ssl.SSLException: Connection reset
解决Android、Flutter编译时Gradle报错:javax.net.ssl.SSLException: Connection reset
144 0
|
3月前
|
C# Android开发 开发者
Android gradle编译时字节码处理
Android gradle编译时字节码处理
44 1
|
Java 开发工具 Android开发
【错误记录】Android Studio 编译报错 ( Invalid Gradle JDK configuration found )
【错误记录】Android Studio 编译报错 ( Invalid Gradle JDK configuration found )
866 0
【错误记录】Android Studio 编译报错 ( Invalid Gradle JDK configuration found )
|
4月前
|
Java Apache Maven
在STS里使用Gradle编译Apache POI5.0.0
在STS里使用Gradle编译Apache POI5.0.0
41 0
|
4月前
|
Java Linux 开发工具
Azkaban【部署 01】Linux环境 CentOS Linux release 7.5.1804安装配置azkaban-3.70.0编译阶段(附安装包及gradle-4.6资源)
【2月更文挑战第4天】Linux环境 CentOS Linux release 7.5.1804安装配置azkaban-3.70.0编译阶段(附安装包及gradle-4.6资源)
106 1
|
4月前
|
开发工具 Android开发 开发者
Android 项目编译 Gradle 配置说明
Android 项目编译 Gradle 配置说明
250 0
|
11月前
|
Java 数据库连接 API
Gradle依赖管理:编译时和运行时依赖的区别
Gradle依赖管理:编译时和运行时依赖的区别
100 0
|
Java 开发工具 Maven
Android 编译 gradle 内存 OOM 解决之路(二)
Android 编译 gradle 内存 OOM 解决之路
|
Java Android开发
Android 编译 gradle 内存 OOM 解决之路(一)
Android 编译 gradle 内存 OOM 解决之路