目前国内对Android领域的探索已经越来越深,不少技术领域如插件化、热修复、构建系统等都对Gradle有迫切的需求,不懂Gradle将无法完成上述事情。所以Gradle必须要学习。

Gradle 里的几乎任何东西都是基于这两个基础概念:

  • task
  • project

掌握了这两个,你就掌握了一大半的 Gradle 知识了。

首先讲 Task

字面理解为任务,Gradle 中所有执行的事件都是借由 Task 执行的。

例如我们新建一个 Android 工程,在其根目录中输入:

gradle tasks -q

可以看到如下输出(你可能需要事先配置gradle的环境变量,或也可使用./gradlew替代):

根据上图可以看到当前工程中的每条task都已罗列出,并且有黄色的输出表示当前task的描述。

其中-q表示忽略gradle本身的log信息,加上这个参数可以屏蔽很多无关的输出,不加也不会影响执行。

Task声明格式

声明一个 task 只需要在任务名前面加上task就可以了,例如下面声明了一个hello的Task。

task hello

通常我们会给task附带一些执行动作,称之为Action,例如

hello.doFirst{
println "hello first"
} hello.doLast{
println "hello last"
}

也可以附带一个闭包配置,称之为Configuration,闭包中不仅可用做赋值操作,也可以执行一些自动执行的配置。

hello {
println "hello"
}

Task依赖

单独声明一个task在实际开发中几乎不会有任何的意义,更多的时候是让多个task组合起来,一个依赖另一个,形成一连串的任务集。

task hello

hello.doFirst{
println "hello "
} task world(dependsOn: "hello") << {
println "world"
}

上面这段代码定义了两个task,当我们执行hello任务的时候,会输出 hello,而执行world任务的时候,由于声明了dependsOn: "hello",表示world依赖hello,会先执行hello,再执行world。

task xxx << {
}

这样的语法等价于

task xxx
xxx.dolast {
}

你可以在任意位置新建一个名为build.gradle的文本,来练习上面讲述的task定义与依赖。

接着讲 Project

Android

├──app
│ └──build.gradle

├──library
│ └──build.gradle

├──*.properties

├──build.gradle

└──setting.gradle

一个 Android 工程,通常是由上述结构构成,其中有着许多不为人知的巧妙用法。

setting.gradle文件

关于setting.gradle中也可以写代码,是很多人不知道的。如下代码是我在上一篇文章【企业级 Android 模块化平台设计建议】中讲到的一个例子,在setting.gradle文件中,可以指定一个project位置,这里就可以将一个外部工程中的模块导入到APP工程中了。

getLocalProperties().entrySet().each { entry ->
def moduleName = entry.key
if (Boolean.valueOf(entry.value)) {
def file = new File(rootProject.projectDir.parent, "/${moduleName.replace("\\W", "")}/${moduleName.toLowerCase()}")
if (file.exists()) {
include ":${moduleName.toLowerCase()}"
project(":${moduleName.toLowerCase()}").projectDir = file
}
}
}

build.gradle

一个项目的根gradle文件,用于描述这个项目的统一资源,其中包括各子资源的使用方式、插件的依赖环境等等。

subprojects{
apply plugin: 'com.android.library'
dependencies {
compile 'com.xxx.xxx:xxx:1.0.0'
}
}

通常我们在每个模块都会引用的 aar 的时候,都会在每个模块里面都去手动的compile一遍,例如support包。 但实际上有一个非常简单的办法,写一遍就可以了,就是在项目的根gradle文件中的subprojects闭包中声明这个dependencies

通常在写compile依赖的时候,我们都会写成这样:

compile 'com.android.support:appcompat-v7:25.0.0'

其实在gradle中,这是一个方法调用,它的本质是compile()方法传入了一个map参数,因此完整的写法实际上是这样的:

compile group: 'com.android.support' name:'appcompat-v7' version:'25.0.0'

同时,map 的可使用 key 不只是有常用的groupnameversion,还包括不常用的configurationclassifier等等。

再看Task

Groovy 是基于 Java 的,只不过在这基础上加了一大堆的闭包,来帮助更方便的开发构建脚本。如果你不会 Groovy,没关系,当成 Java 写就行了,其实当成 Kotlin 写是最恰当的。如果你还不会 Kotlin,我强烈推荐你查看我的 【 Kotlin Primer 】系列文章

每个Task都可以配置其输入与输出,如果一个Task的输出与上一次的输出一致,则不会重复执行。此刻,会在命令行中输出UP-TO-DATE表示已经是最新的结果。

例如如下Task:

task transform {
ext.srcFile = file('hello.txt')
ext.destDir = new File(buildDir, 'generated')
inputs.file srcFile
outputs.dir destDir
doLast {
destDir.mkdirs()
def ins = new BufferedReader(new FileReader(srcFile))
def stringBuilder = new StringBuilder()
def temp
while ((temp = ins.readLine()) != null) {
stringBuilder.append(temp)
}
def destFile = new File(destDir, "world.txt")
destFile.text = stringBuilder.toString()
}
}

重复执行后会输出UP-TO-DATE

骚操作的背后

学习任何一门技术,最快的途径就是看源码,gradle的源码位于src目录中,例如在我本机的路径为:

/Users/zhangtao/.gradle/wrapper/dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3/src

本地新建一个普通Java工程,导入源码查看代码与注释,这是最好的学习资料。

task hello

在 Groovy 中,方法括号可以省略,如果字符串的类型是可以被推断的,那么引号也可以省略

public interface org.gradle.api.Project{
Task task(String name);
Task task(String name, Closure configureClosure);
} // TaskFactory
public TaskInternal createTask(Map<String, ?> args) {
}

闭包的存在,目的是为了更好的为对象初始化。同 Kotlin 一样,当闭包做为最后一个参数的时候,可以省略括号。

Copy a = task(myCopy, type: Copy)
a.from 'resources'
a.into 'target'
a.include('**/*.txt', '**/*.xml', '**/*.properties')

等价于

task myCopy(type: Copy)

myCopy {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}

本章就讲到这里,下一篇讲如何创建一个Gradle插件,完成编译时向指定类或新生成类中动态添加代码(包括jar包中)。

往期回顾:

最新文章

  1. 重走java--Step 2
  2. Java解析网段下包含的所有IP地址
  3. do-while语句
  4. 添加 Gradle 依赖与 build.gradle 配置初识
  5. Salt安装
  6. Strange Towers of Hanoi
  7. C#判断操作系统是32位还是64位(超简单)
  8. MYSQL LIKE 区分大小写匹配
  9. linux学习之linux的hostname修改详解《转》
  10. View的呈现(一)ActionResult
  11. Linux - 简明Shell编程12 - 定制输出(ColorOutput)
  12. Ubuntu安装与配置KVM
  13. Linux之环境搭建(一)
  14. oracle 在已有表新增列内批量加数据
  15. FFmpeg编解码处理2-编解码API详解
  16. 卡方分布 | t检验 | F检验 | 卡方检验 | 假设检验 | 各种检验持续总结
  17. Spring中内置的一些工具类
  18. 从零自学Java-5.使用条件测试进行判断
  19. 【Amazon 必考】Amazon Leadership Principles 亚马逊领导力准则
  20. Go语言学习笔记(四)结构体struct &amp; 接口Interface &amp; 反射reflect

热门文章

  1. nginx服务跳转
  2. QObject::startTimer: timers cannot be started from another thread 一则输出调试:关于QT的线程关联性
  3. 二维动态规划&amp;&amp;二分查找的动态规划&amp;&amp;最长递增子序列&amp;&amp;最长连续递增子序列
  4. AcWing 1250. 格子游戏
  5. 信息论估计工具jidt基本使用
  6. 其他:IDEA插件无法安装——网络代理设置
  7. pip与apt-get的使用
  8. Spring Cloud中,如何解决Feign/Ribbon第一次请求失败的问题?
  9. PHP Kafka 消息队列使用
  10. LVM磁盘管理实战案例