6个技巧加速你的gradle编译

本片文章翻译自:

最近我们都在讨论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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容