Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具,gradle跟ant、maven一样,是一种依赖管理、自动化构建工具。但跟ant、maven不一样的是,它并没有使用xml语言,而是采用了Groovy语言,这使得它更加简洁、灵活,更加强大的是,gradle完全兼容maven和ivy。详细介绍可以查看官网的介绍:http://www.gradle.org/
在学习Gradle之前,先看一下Groovy的介绍。
Groovy环境配置非常简单,根据Groovy官网 的介绍,安装的时候使用如下的命令即可安装Groovy。
curl -s get.gvmtool.net | bash
source "$HOME/.gvm/bin/gvm-init.sh"
gvm install groovy
然后我们新建一个以xxx.groovy结尾的文件,然后在里面添加如下代码:
println "hello groovy"
然后使用如下的命令运行即可。
groovy xxx.groovy
groovy除了支持jdk之外,groovy还提供GDK,相关API请查看GDK官网。
Groovy语法介绍 1,Groovy为了尽量减少代码的输入,可以不使用分号结尾。 2,Groovy支持动态类型,即定义变量的时候可以不指定其类型,为了代码的清晰,变量定义常常使用关键字def。例如:
def variable1 = 1 //可以不使用分号结尾
def varable2 = "I am a person"
def int x = 1 //变量定义时,也可以直接指定类型
3,添加函数时,参数的类型也可以不指定。例如:
String testFunction(arg1,arg2){
...
}
4,Groovy对字符串支持相当强大,充分吸收了一些脚本语言的优点。单引号”中的内容严格对应Java中的String,不对$符号进行转义。例如:
def singleQuote='I am $ dolloar' //输出就是I am $ dolloar
对于双引号”“的内容,如果字符中有号的话,则它会号的话,则它会 表达式 先求值。例如:
def doubleQuoteWithoutDollar = "I am one dollar" //输出 I am one dollar
def x = 1
def doubleQuoteWithDollar = "I am $x dolloar" //输出I am 1 dolloar
5,Groovy中函数调用可以不加括号。例如:
Groovy中函数调用的时候还可以不加括号
作为面向对象的动态语言,Groovy有诸多面向对象的特性。不过和Java等面向对象的语言不通的是,Java使用基本数据类型,而Groovy则使用基本数据类型的包装数据类型。
Groovy支持的容器类型主要有三类:List、Map、Range。
List
def aList = [5,'string',true] //定义一个List,其元素可以是任何对象。如果索引超过当前链表长度,List会自动扩展数组的大小。 数据索引从0开始。
assert aList[1] == 'string'
assert aList[5] == null //第6个元素为空
aList[100] = 100 //设置第101个元素的值为100
assert aList[100] == 100
//此时,aList的大小为101
println aList.size ===>101
Range Range是Groovy对List的一种拓展。例如:
def aRangeWithoutEnd = 1..<5
println aRange.from
println aRange.to
闭包,英文名Closure。是一种典型的数据类型,代表了一段可执行的代码。
def aClosure = {
Stringparam1, int param2 ->
println"this is code"
}
闭包定义好后,要调用它的方法就是:闭包对象.Call或者闭包对象(参数)。例如:
aClosure.call("this is string",100)或者
aClosure("this is string", 100)
注:如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫it,和this的作用类似。例如:
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
闭包在Groovy中大量使用,比如很多类都定义了一些函数,这些都是闭包。
public static <T> List<T> each(List<T> self, Closure closure)
1,在Groovy中,当函数的最后一个参数是闭包的话,可以省略圆括号。例如:
def testClosure(int a1,String b1, Closure closure){
//do something
closure() //调用闭包
}
那么调用的时候,就可以免括号!
testClosure (4, "test", {
println "i am in closure"
} )
上面的代码在调用的时候就可以省略()。
Groovy中可以像Java那样写package,然后写类。比如在文件夹com/cmbc/groovy/目录中放一个文件,叫Test.groovy。如果不声明访问权限的话,Groovy中类及其变量默认都是public的。Groovy可以把自己转换成Java类。例如,将test.groovy转换为Java类的代码为:
groovyc -d classes test.groovy
gradle跟maven一样,也有一个配置文件,maven里面是叫pom.xml,而在gradle中是叫build.gradle,相信Android开发人员对这个都比较了解。Android Studio中的android项目通常至少包含两个build.gradle文件,一个是project范围的,另一个是module范围的,由于一个project可以有多个module,所以每个module下都会对应一个build.gradle。 Gradle只是提供了构建项目的一个框架,真正起作用的是Plugin。Gradle在默认情况下为我们提供了许多常用的Plugin,其中包括有构建Java项目的Plugin,还有War,Ear等。
现在我们都在谈领域驱动设计,Gradle本身的领域对象主要有Project和Task。Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。另外,一个Task可以读取和设置Project的Property以完成特定的操作。让我们来看一个最简单的Task,例如:
task helloWorld << {
println "Hello World!"
}
这里的“<<”表示向helloWorld中加入执行代码——其实就是groovy代码。Gradle向我们提供了一整套DSL,所以在很多时候我们写的代码似乎已经脱离了groovy,但是在底层依然是执行的groovy。比如上面的task关键字,其实就是一个groovy中的方法,而大括号之间的内容则表示传递给task()方法的一个闭包。 在与build.gradle相同的目录下执行:
gradle helloWorld
输出结果如下:
:helloWorld
Hello World!
BUILD SUCCESSFUL
Total time: 2.544 secs
在默认情况下,Gradle将当前目录下的build.gradle文件作为项目的构建文件。在上面的例子中,我们创建了一个名为helloWorld的Task,在执行gradle命令时,我们指定执行这个helloWorld Task。这里的helloWorld是一个DefaultTask类型的对象,这也是定义一个Task时的默认类型,当然我们也可以显式地声明Task的类型,甚至可以自定义一个Task类型。 比如,我们可以定义一个用于文件拷贝的Task:
task copyFile(type: Copy) {
from 'xml'
into 'destination'
}
以上copyFile将xml文件夹中的所有内容拷贝到destination文件夹中。这里的两个文件夹都是相对于当前Project而言的,即build.gradle文件所在的目录。
Task之间可以存在依赖关系,比如taskA依赖于taskB,那么在执行taskA时,Gradle会先执行taskB,然后再执行taskA。声明Task依赖关系的一种方式是在定义一个Task的时候:
task taskA(dependsOn: taskB) {
//do something
}
Gradle在默认情况下为我们提供了几个常用的Task,比如查看Project的Properties、显示当前Project中定义的所有Task等。可以通过一下命令查看Project中所有的Task:
gradle tasks
Gradle 是 Android 现在主流的编译工具,是系统推荐的应用构建方案,虽然在Gradle 出现之前和之后都有对应更快的编译工具出现。 本文将结合Gradle在Android项目中的使用来讲解。 这里借用网络的图片来看一下历史版本中Android是如何编译项目的。如图:
在Gradle出现之前,Android使用ant的方式构建和编译项目,相关的资料可以查看我之前关于ant的介绍。 在Gradle 出现之后,也有新的编译工具出现,就是FaceBook 的Buck工具。这些编译工具在出现的时候大多都比 Gradle 要快,Gradle 之所以慢是跟它的编译周期有很大关系,那为什么Android还是使用Gradle来构建和编译项目呢?只因为Gradle是google的亲儿子。
在上面我们说过, Gradle 中最非常重要的两个对象:Project和Task。 每个项目的编译至少有一个 Project,一个 build.gradle就代表一个project,每个project里面包含了多个task,task 里面又包含很多action,action是一个代码块,里面包含了需要被执行的代码。 在编译过程中, Gradle 会根据 build 相关文件,聚合所有的project和task,执行task 中的 action。因为 build.gradle文件中的task非常多,先执行哪个后执行那个需要一种逻辑来保证。这种逻辑就是依赖逻辑,几乎所有的Task 都需要依赖其他 task 来执行,没有被依赖的task 会首先被执行。所以到最后所有的 Task 会构成一个 有向无环图(DAG Directed Acyclic Graph)的数据结构。 Gradle的编译过程分为三个阶段:
和大家在Android项目看到的一样,对于一个gradle 项目来说必含有三个.gradle文件:一个是项目的setting.gradle,最顶层的 build.gradle文件,每个Module 的build.gradle文件。
setting.gradle 对于 setting 文件定义了哪些module 应该被加入到编译过程,对于单个module 的项目可以不用需要这个文件,但是对于 multimodule 的项目我们就需要这个文件,否则gradle 不知道要加载哪些项目。 顶层的build.gradle 顶层的build.gradle文件的配置最终会被应用到所有项目中。配置如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
allprojects{
repositories{
jcenter()
}
}
说明: buildscript:定义了 Android 编译工具的类路径。repositories中,jCenter是一个著名的 Maven 仓库。 allprojects:中定义的属性会被应用到所有 moudle 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。 moudle的build.gradle 每一个moudle都有自己的build.gradle,和顶层的build.gradle格式相同。
说明: buildTypes:定义了编译类型,针对每个类型我们可以有不同的编译配置,不同的编译配置对应的有不同的编译命令。默认的有debug、release 的类型。 dependencies:是属于gradle 的依赖配置。它定义了当前项目需要依赖的其他库。
Gradle有四个基本的Task,Android继承他们并做了一定的补充。
除此之外还有一些常用的新增的其他命令,比如 install命令,会将编译后的apk 安装到连接的设备。
我们运行的许多命令除了会输出到命令行,还会在build文件夹下生产一份运行报告。比如check命令会生成lint-results.html.在build/outputs中。
对于这个大家肯定不会陌生,最常用的用法就是通过BuildConfig.DEBUG来判断当前的版本是否是debug版本,如果是就会输出一些只有在 debug 环境下才会执行的操作。
Repositories 就是代码仓库,这个相信大家都知道,我们平时的添加的一些 dependency 就是从这里下载的,Gradle 支持三种类型的仓库:Maven,Ivy和一些静态文件或者文件夹。在编译的执行阶段,gradle 将会从仓库中取出对应需要的依赖文件,当然,gradle 本地也会有自己的缓存,下次就不用去仓库里面取了。
gradle 支持多种 Maven 仓库,一般我们就是用共有的jCenter就可以了。如果用到私有的仓库,也可以手动加入(如果需要账号的则需要配置用户名和密码)。
在引用库的时候,每个库必须包含三个元素:组名:库名称:版本号。 如果我们要保证我们依赖的库始终处于最新状态,我们可以通过添加通配符的方式。
如果涉及到本地的库,还可以添加本地库。通过files()方法可以添加文件依赖,如果有很多jar文件,我们也可以通过fileTree()方法添加一个文件夹。
如果涉及到配置本地 .so库。在配置文件中做如下配置,然后在对应位置建立文件夹,加入对应平台的.so文件。