Gradle详解

Gradle简介

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具,gradle跟ant、maven一样,是一种依赖管理、自动化构建工具。但跟ant、maven不一样的是,它并没有使用xml语言,而是采用了Groovy语言,这使得它更加简洁、灵活,更加强大的是,gradle完全兼容maven和ivy。详细介绍可以查看官网的介绍:http://www.gradle.org/

Gradle入门

在学习Gradle之前,先看一下Groovy的介绍。

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

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数据类型

基本数据类型

作为面向对象的动态语言,Groovy有诸多面向对象的特性。不过和Java等面向对象的语言不通的是,Java使用基本数据类型,而Groovy则使用基本数据类型的包装数据类型。

容器

Groovy支持的容器类型主要有三类:List、Map、Range。

  • List:链表,其底层对应Java中的List接口,一般用ArrayList作为真正的实现类。
  • Map:键-值表,其底层对应Java中的LinkedHashMap。
  • Range:范围,它其实是List的一种拓展。

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

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

Android Gradle

Gradle 是 Android 现在主流的编译工具,是系统推荐的应用构建方案,虽然在Gradle 出现之前和之后都有对应更快的编译工具出现。 本文将结合Gradle在Android项目中的使用来讲解。 这里借用网络的图片来看一下历史版本中Android是如何编译项目的。如图:

在Gradle出现之前,Android使用ant的方式构建和编译项目,相关的资料可以查看我之前关于ant的介绍。 在Gradle 出现之后,也有新的编译工具出现,就是FaceBook 的Buck工具。这些编译工具在出现的时候大多都比 Gradle 要快,Gradle 之所以慢是跟它的编译周期有很大关系,那为什么Android还是使用Gradle来构建和编译项目呢?只因为Gradle是google的亲儿子。

Gradle生命周期

在上面我们说过, 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的编译过程分为三个阶段:

  • 初始化阶段:创建 Project 对象,如果有多个build.gradle,也会创建多个project.
  • 配置阶段:在这个阶段,会执行所有的编译脚本,同时还会创建project的所有的task,为后一个阶段做准备。
  • 执行阶段:在这个阶段,gradle 会根据传入的参数决定如何执行这些task,真正action的执行代码就在这里.

和大家在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 的依赖配置。它定义了当前项目需要依赖的其他库。

Android tasks

Gradle有四个基本的Task,Android继承他们并做了一定的补充。

  • assemble:对所有的 buildType 生成 apk 包。
  • clean:移除所有的编译输出文件,比如apk
  • check:执行lint检测编译。
  • build:同时执行assemble和check命令 这些都是基本的命令,在实际项目中会根据不同的配置,会对这些task 设置不同的依赖。比如 默认的 assmeble 会依赖 assembleDebug 和assembleRelease,如果直接执行assmeble,最后会编译debug,和release 的所有版本出来。如果我们只需要编译debug 版本,我们可以运行assembleDebug。

除此之外还有一些常用的新增的其他命令,比如 install命令,会将编译后的apk 安装到连接的设备。

我们运行的许多命令除了会输出到命令行,还会在build文件夹下生产一份运行报告。比如check命令会生成lint-results.html.在build/outputs中。

BuildConfig

对于这个大家肯定不会陌生,最常用的用法就是通过BuildConfig.DEBUG来判断当前的版本是否是debug版本,如果是就会输出一些只有在 debug 环境下才会执行的操作。

Repositories

Repositories 就是代码仓库,这个相信大家都知道,我们平时的添加的一些 dependency 就是从这里下载的,Gradle 支持三种类型的仓库:Maven,Ivy和一些静态文件或者文件夹。在编译的执行阶段,gradle 将会从仓库中取出对应需要的依赖文件,当然,gradle 本地也会有自己的缓存,下次就不用去仓库里面取了。

gradle 支持多种 Maven 仓库,一般我们就是用共有的jCenter就可以了。如果用到私有的仓库,也可以手动加入(如果需要账号的则需要配置用户名和密码)。

Dependencies

在引用库的时候,每个库必须包含三个元素:组名:库名称:版本号。 如果我们要保证我们依赖的库始终处于最新状态,我们可以通过添加通配符的方式。

Local dependencies

如果涉及到本地的库,还可以添加本地库。通过files()方法可以添加文件依赖,如果有很多jar文件,我们也可以通过fileTree()方法添加一个文件夹。

如果涉及到配置本地 .so库。在配置文件中做如下配置,然后在对应位置建立文件夹,加入对应平台的.so文件。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JackieZheng

Spring实战——缓存

缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis…… 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓...

21010
来自专栏IT可乐

Spring详解(四)------注解配置IOC、DI

  Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以‘@注解名’在代码中存在...

2067
来自专栏JackieZheng

Spring实战——缓存

缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis…… 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓...

22010
来自专栏编程心路

Python日志库logging总结-可能是目前为止将logging库总结的最好的一篇文章

在部署项目时,不可能直接将所有的信息都输出到控制台中,我们可以将这些信息记录到日志文件中,这样不仅方便我们查看程序运行时的情况,也可以在项目出现故障时根据运行时...

15.2K10
来自专栏JackieZheng

Spring实战——缓存

缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis…… 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓...

19610
来自专栏java系列博客

深入理解Java内存模型(一)——基础

1704
来自专栏Java 技术分享

一篇 SpringData+JPA 总结

1543
来自专栏13blog.site

MyBatis + MySQL返回插入成功后的主键id

这是最近在实现perfect-ssm中的一个功能时碰到的一个小问题,觉得需要记录一下,向MySQL数据库中插入一条记录后,需要获取此条记录的id值,以生成对应的...

3256
来自专栏奔跑的蛙牛技术博客

javaBean 简单理解JavaBean简单及使用

PO:persistant object持久对象,可以看成是与数据库中的表相映射的java对象。最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用P...

1594
来自专栏知识分享

关于原子哥ENC28J60网络通信模块接收数据代码的一点疑惑

---恢复内容开始--- 这几天做STM32的ENC28J60网络通信模块,自己在原子哥的代码上进行修改测试,,发现一个问题,电脑和板子进行通信的时候总隔一段时...

3648

扫码关注云+社区

领取腾讯云代金券