目前国内对 Android 领域的探索已经越来越深, 不少技术领域如插件化热修复构建系统等都对 Gradle 有迫切的需求, 不懂 Gradle 将无法完成上述事情所以 Gradle 必须要学习
对本文有任何问题, 可加我的个人微信: kymjs123
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 包中)
本文建议零售价: 10.24 元
去支付
来源: https://kymjs.com/code/2018/02/25/01/