本文概述
前言
我认为Gradle是一个很好的构建工具,但是我不认为它不是Android开发中一个最好的构建工具——因为我刚开始的时候看不懂这build.gradle文件,首先它不像pom.xml那样有非常严格的xml格式,似乎又不像JSON,这真是头痛,而是那时候我还刚编程没多久,这构建配置太灵活了!
另外一个原因是因为Gradle脚本其实需要你学一下Groovy才行,虽然说Groovy支持Java,但是它常使用的方式还是Groovy自己的新特性。所以在学习gradle之前请务必学一下Groovy,这个在上一篇已经讨论了,这里假设大家已经有Groovy的基础了。
Gradle:构建工具的目标
学Gradle也不能盲目地为了学而学,否则学完也没什么用。所以Gradle存在的必要是什么?Gradle是一个构建工具,而构建工具需要至少处理一下几个任务:
- 配置仓库:有一些构建工具不支持,但Gradle支持仓库和包依赖管理,所以我们需要在Gradle进行仓库的配置。
- 依赖配置:一般项目都需要常使用第三方库,这个比较简单。
- 构建生命周期:一般构建工具都有项目构建生命周期,例如清理、编译、测试、打包、发布等。
- 构建策略:我们可以针对项目构建生命周期编写一些构建策略,另外还可以面对最终的产品进行编写策略。
Groovy和Gradle的关系
Groovy是一个DSL(特定领域语言),编写的代码使用JVM运行,Gradle是一个构建程序,主要使用build.gradle文件,该文件基于Groovy,你可以把该文件当做groovy源码文件,所以在该配置文件中编写的都是groovy代码,这相当于我们在动态地自定义编写构建代码。
Gradle也是一个Java或Groovy程序,而Gradle也提供一些Groovy API,所以在编写构建脚本的时候,除了可以使用Groovy或Java的特性外,还主要使用Gradle中的API。也就是说:Gradle提供了Groovy开发的API,相当于第三方的Groovy SDK。
综上,编写Gradle构建脚本除了要参考Groovy API,还要参考Gradle提供的API,而且在Android开发中,还要参考Android Grandle Plugin的API。
再次综合,所以你看,使用gradle构建项目是多么的困难和麻烦,如果你没有一些技术基础,那么编写起来可能就只有复制粘贴了,像我以前一样。
一个好的忠心建议:不要试图把Gradle完全学好,你做不到的,我们需要的是把它用好,而不是试图把它学精。这是我的个人经验,用得好就行了,不然这会浪费很多精力和时间,人生苦短,能用就好。
安装Gradle
Gradle可以在所有主要的操作系统上运行,并且只需要Java开发工具包版本8或更高就可以运行。要检查,请运行java -version,请确保你已经安装了JDK。
Gradle自带Groovy库,因此不需要安装Groovy。任何现有的Groovy安装都会被Gradle忽略。
Gradle使用它在路径中找到的任何JDK。或者,您可以设置JAVA_HOME环境变量,使其指向所需JDK的安装目录。
类Unix系统安装Gradle比较容易,例如我们可以使用sdk install gradle或brew install gradle。在windows上安装gradle的步骤如下:
- 从官网https://gradle.org/releases/下载最新版的Gradle,选择“binary-only”的版本进行下载。
- 创建一个目录,如G:/Gradle,把下载的zip包中的文件解压到该文件夹中。
- 配置环境变量:将Gradle安装文件的bin目录配置到环境变量Path中,如:G:\Gradle\gradle-6.8.1\bin。
- 验证安装:在命令行中输入gradle -v,你可以看到gradle的相关信息,即表示安装成功。
运行Gradle构建
Gradle构建环境
Gradle首先是一个构建应用程序,它是一个JVM程序,该程序带有一些系统定义的命令行参数。我们可以为Gradle程序配置一些运行参数,例如内存大小、缓存等等,而配置这些参数有几种方式(按照优先级从大到小):
- 在命令行中指定系统属性,例如-Dgradle.user.home。
- GRADLE_USER_HOME的gradle.properties文件。
- 项目根目录的gradle.properties文件。
- Gradle安装目录中的gradle.properties文件。
- 下面是一些常见的gradle运行环境配置,通常我们可以在项目根目录的gradle.properties中配置,或者在安装目录中全局配置。
- org.gradle.caching=(true,false):当设置为true时,Gradle将重用以前任何版本的任务输出,如果可能的话,结果将是更快的构建。
- org.gradle.configureondemand=(true,false):允许潜在配置随需应变,Gradle将尝试只配置必要的项目。
- org.gradle.console=(auto,plain,rich,verbose):自定义控制台输出颜色或冗长。
- 默认值取决于Gradle如何被调用。
- org.gradle.daemon=(true,false):当设置为true时,将使用Gradle守护进程来运行构建。默认是true。
- org.gradle.java.home=(path to JDK home):指定Gradle构建过程的Java home。取值为jdk或jre然而,位置取决于你的构建,使用JDK更安全。
- 一个合理的默认如果设置未指定,则从您的环境(JAVA_HOME或到java的路径)派生。这不会影响用于启动Gradle客户端VM的Java版本
- org.gradle.jvmargs=(JVM arguments):指定用于Gradle守护进程的JVM参数。
- 该设置特别适用于为构建性能配置JVM内存设置。这不会影响JVM设置,用于Gradle客户端VM。
更多配置属性可以参考官方文档,另外,在properties中配置的内容,在build.gradle中可以直接访问。
Gradle守护程序
守护进程是作为后台进程运行的计算机程序,不受交互用户的直接控制。
Gradle运行在Java虚拟机(JVM)上,并使用几个需要大量初始化时间的支持库。因此,它有时似乎开始得有点慢。这个问题的解决方案是Gradle守护进程:一个长期存在的后台进程,它执行构建的速度比其他方式快得多。我们通过避免昂贵的引导过程和利用缓存来实现这一点,并将项目的数据保存在内存中。使用这个守护进程运行Gradle构建与不使用这个守护进程没有什么不同。只要简单地配置你是否想要使用它,其他的一切都是由Gradle透明地处理的。
默认情况下,Gradle守护进程是启用的,我们建议始终启用它。你可以通过命令行选项--no-daemon禁用长期存在的Gradle守护进程,或者添加org.gradle。daemon=false to your gradle。属性文件。您可以在下面的Daemon FAQ中找到禁用(和启用)这个守护进程的其他方法的详细信息。
守护进程是一个后台进程。不过,您不必担心在您的机器上构建Gradle进程。每个守护进程都监控自己的内存使用情况,并将其与系统总内存进行比较,当可用的系统内存较低时,守护进程将在空闲时停止自身。如果你想显式地停止运行守护进程,只要使用gradle --stop命令即可。
初始化脚本
Gradle提供了一个强大的机制,允许基于当前环境定制构建。这种机制也支持那些希望与Gradle集成的工具。
初始化脚本(也叫init脚本)类似于Gradle中的其他脚本。然而,这些脚本是在构建开始之前运行的。
有几种方法可以使用初始化脚本:
- 在命令行上指定一个文件。命令行选项为-I或--init-script,后跟脚本的路径。命令行选项可以出现不止一次,每次都添加另一个init脚本。如果命令行上指定的任何文件不存在,则生成将失败。
- 放置一个名为init.gradle的文件(或init.gradle.kts for Kotlin),在USER_HOME/.gradle /目录中。
- 放置一个以.gradle(或.init.gradle.kts for Kotlin)结尾的文件,在USER_HOME / .gradle / init.d /目录中。
- 放置一个以.gradle(或.init.gradle)结尾的文件。在GRADLE_HOME/init.d/目录,在Gradle发行版。这允许你打包一个自定义的Gradle发行版包含一些自定义构建逻辑和插件。您可以将其与Gradle包装器结合使用作为一种方式,使自定义逻辑可用于企业中的所有构建。
如果找到多个初始化脚本,它们都将按照上面指定的顺序执行。给定目录中的脚本按字母顺序执行。例如,这允许工具在命令行上指定init脚本,用户将其放在自己的主目录中定义环境,并且两个脚本都将在执行Gradle时运行。
编写初始化脚本
与Gradle构建脚本类似,初始化脚本是Groovy或Kotlin脚本。每个初始化脚本都有一个Gradle实例与它相关联。init脚本中的任何属性引用和方法调用都将委托给这个Gradle实例。
每个初始化脚本还实现了Script接口。
例如下面是build.gradle文件:
repositories {
mavenCentral()
}
task showRepos {
doLast {
println "All repos:"
println repositories.collect { it.name }
}
}
下面是init.gradle文件:
allprojects {
repositories {
mavenLocal()
}
}
运行初始化脚本需要使用—init-script指定该脚本文件,例如:
gradle --init-script init.gradle -q build
初始化脚本的外部依赖关系
在构建脚本的外部依赖项中,解释了如何向构建脚本添加外部依赖项。初始化脚本也可以声明依赖关系。您可以使用initscript()方法来实现这一点,传递一个声明初始化脚本类路径的闭包。
initscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.apache.commons:commons-math:2.0'
}
}
传递给initscript()方法的闭包配置ScriptHandler实例。通过向类路径配置添加依赖项来声明初始化脚本类路径。例如,这与声明Java编译类路径的方法相同。可以使用声明依赖项中描述的任何依赖项类型,但项目依赖项除外。
声明了初始化脚本类路径后,就可以像使用类路径上的任何其他类一样使用初始化脚本中的类。下面的示例添加了前面的示例,并使用了初始化脚本类路径中的类。
import org.apache.commons.math.fraction.Fraction
initscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.apache.commons:commons-math:2.0'
}
}
println Fraction.ONE_FIFTH.multiply(2)
你可以看到classpath方法配置的依赖仅仅用于脚本编码使用,而非用于当前项目开发使用(例如Android项目)。
编写Gradle构建脚本
构建脚本基础知识
每个Gradle构建都是由一个或多个项目组成的。一个项目代表什么取决于你用Gradle做了什么。例如,一个项目可能代表一个库JAR或一个web应用程序。它可能代表由其他项目产生的jar组装而成的分发ZIP。项目并不一定代表要建造的东西。它可能表示要做的事情,比如将应用程序部署到登台或生产环境中。如果现在这看起来有点模糊,不要担心。Gradle的按约定构建支持为什么是项目增加了一个更具体的定义。
Gradle在项目中所能做的工作是由一个或多个任务定义的。任务表示构建执行的某些原子工作。这可能是编译一些类、创建一个JAR、生成Javadoc,或者向存储库发布一些存档。
通常,任务是通过应用插件来提供的,这样您就不必自己定义它们。不过,为了让你了解什么是任务,我们将在本章的一个项目构建中定义一些简单的任务。
您可以使用Gradle命令运行Gradle构建。gradle命令查找一个名为build的文件。gradle在当前目录下。[1:有命令行开关来改变这种行为。我们称之为构建。gradle文件是一个构建脚本,尽管严格来说它是一个构建配置脚本,我们将在后面看到。构建脚本定义了一个项目及其任务。
要尝试这一点,创建以下名为build.gradle的构建脚本。
第一个构建脚本
创建一个build.gradle的脚本,输入以下内容:
tasks.register('hello') {
doLast {
println 'hello gradle'
}
}
运行命令:gradle hello,该任务会被执行,这是创建gradle任务的一种方式,其中tasks是project的属性,register是一个方法,“hello”为任务名称,后面是一个闭包,闭包包含具体的内容。
你可能已经猜到,可以声明依赖于其他任务的任务,使用dependsOn指定。如果A人物依赖于B任务,那么使用gradle执行A任务的时候会先执行B任务,下面是一个例子:
project.tasks.register('hello') {
doFirst {
println 'hello do first'
}
doLast {
println 'hello do last'
}
}
tasks.register('greet') {
dependsOn tasks.hello
doLast {
println 'greeting!'
}
}
默认任务
Gradle允许你定义一个或多个默认任务,如果没有指定其他任务,这些任务将被执行。
task clean {
doLast {
println 'clean project files'
}
}
task build {
doLast {
println 'build project'
}
}
task test {
doLast {
println 'test project'
}
}
defaultTasks 'clean', 'build'
这相当于运行gradle clean build。在多项目构建中,每个子项目都可以有自己特定的默认任务。如果子项目没有指定默认任务,则使用父项目的默认任务(如果已定义)。
构建脚本的外部依赖项
这和初始化脚本中的是一样的,配置相应的仓库和依赖,其依赖是用于脚本编写的,而不是用于真正的项目编写。
如果您的构建脚本需要使用外部库,您可以将它们添加到构建脚本本身的脚本类路径中。这需要使用buildscript()方法,传入一个声明构建脚本类路径的块。
import org.apache.commons.codec.binary.Base64
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version:
'1.2'
}
}
task encode {
doLast {
def byte[] encodedString = new Base64().encode('hello world\n'
.getBytes())
println new String(encodedString)
}
}
传递给buildscript()方法的块配置了一个ScriptHandler实例。通过向类路径配置添加依赖项来声明构建脚本的类路径。例如,这与声明Java编译类路径的方法相同。您可以使用除项目依赖项以外的任何依赖项类型。
对于多项目构建,使用项目的buildscript()方法声明的依赖项对其所有子项目的构建脚本可用。
编写任务
当Gradle执行一个任务时,它可以在控制台UI中或通过工具API给任务贴上不同结果的标签。这些标签是基于一个任务是否有要执行的操作,它是否应该执行这些操作,它是否执行了这些操作,以及这些操作是否进行了任何更改。
- (no label) or EXECUTED:任务已经执行它的动作。
- UP-TO-DATE:任务的输出没有改变。
- FROM-CACHE:任务的输出可以从以前的执行中找到。
- SKIPPED:任务没有执行其操作。
- NO-SOURCE:任务不需要执行其操作。
在本章中,我们已经看到了如何使用字符串来定义任务名。在某些情况下,您可能需要使用这种样式的一些变体。
def hello = tasks.register('hello') {
doLast {
println 'hello'
}
}
def copy = tasks.register('copy', Copy) {
from(file('./sources'))
into(buildDir)
}
def clean = tasks.register('clean') {
file(buildDir).listFiles().each { file ->
file.delete()
}
}
注意:如果您查看tasks容器的API,您可能会注意到还有用于创建任务的其他方法。不鼓励使用这些方法,并将在未来的版本中弃用。这些方法的存在只是为了向后兼容,因为它们是在任务避免配置添加到Gradle之前引入的。
总结
本文就先到这里了,更多可参考官方参考文档,其中有很详细的内容,本文后面有一部分是摘自这个参考文档的,要说的重点都在前面一部分,想来后面一部分没什么特别要说的,所以就不继续写了。我觉得Gradle的参考文档求其使用上描述得是不够好的,例如关于任务的配置,把简单的东西搞得很复杂,反正我看得很头晕(可能太困了)。
本文还有一个没说的是Gradle DSL参考文档,首先你要知道Gradle的一些基本原理,然后基本就可以使用了,而使用的详细内容需要参考Gradle DSL,对于Android平台还需要参考Android Plugin DSL。
好了,先到这里了,有机会有重点内容再写吧,对于一些只需列出来就行了的,写起来就感觉没什么内容。