Gradle - DSL

Gradle 系列

  1. Gradle - 简介
  2. Gradle - Groovy Language
  3. Gradle - DSL
  4. Gradle - Android Plugin DSL
  5. Gradle - 插件开发
  6. Gradle - 插件发布

Gradle DSL

Gradle 是一个编译打包工具,但实际上它也是一个编程框架。Gradle 有自己的 API 文档,对应链接如下:

由此可以看出,编写 Gradle 脚本时,我们实际上就是调用 Gradle 的 API 编程。

基本组件

  • Project

    Gradle 中,每一个待编译的工程都是一个 Project。

    例如:用 AndroidStudio 构建项目时,每一个 module 都是 Gradle 定义的 Project。

  • Task

    每一个 Project 在构建的时候都包含一系列的 Task。

    比如,一个 Android APK 的编译可能包含:Java 源码编译 Task、资源编译 Task、JNI 编译 Task、lint 检查 Task、打包生成 APK 的 Task、签名 Task 等。

    对于 Gradle 的编译打包流程而言,Task 就是最小的执行单元,其中将调用具体的函数来完成实际的工作。

  • Plugin

    一个 Project 到底包含多少个 Task,在很大程度上依赖于编译脚本指定的插件。插件定义了一系列基本的 Task,并指定了这些 Task 的执行顺序。

    整体来看,Gradle 作为一个框架,负责定义通用的流程和规则;根据不同的需求,实际的编译工作则通过具体的插件来完成。

    例如:编译 Java 项目时使用 Java 插件、编译 Groovy 项目时使用 Groovy 插件、编译 Android App 有 Android App 插件,编译 Android Library 有 Android Library 插件。

    举个例子来说,我们在 Android APK 对应的 build.gradle 中经常可以看到如下代码:

    apply plugin: 'com.android.application'
    

    这就是使用编译 APK 的插件。

    同样,在编译 Android Library 时可以看到如下代码,用于指定使用编译 Library 的插件:

    apply plugin: 'com.android.library'
    

    到现在为止,我们知道每一个 Library 和每一个 App 都是单独的 Project。根据 Gradle 的要求,每一个 Project 在其根目录下都需要有一个 build.gradlebuild.gradle 文件就是该 Project 的编译脚本,类似于 Makefile。

构建过程

新建一个 Android 项目,目录结构如下:

ProjectName
	|-app
		|-build
		|-lib
		|-src
		|-build.gradle	//后面讨论
	|-library-test
		|-build.gradle	//后面讨论
	|-gradle
		|-wrapper
	|-build.gradle	//*
	|-settings.gradle	//*

通过目录结构可以看出来,每一个 Project 中都有一个 build.gradle 文件,里面的内容后面再介绍。

在上面项目中有 applibrary-test 两个 Project,如果编译某个 Project 则需要 cd 到某个 Project 目录中。比如,cd xxx/app 然后执行 gradle xxx xxx 是 task 的名字。

这很麻烦啊,有 10 个独立 Project,就得重复执行 10 次这样的命令。更有甚者,所谓的独立 Project 其实有依赖关系的。那么,我想在项目目录下,直接执行 gradle xxx 是否能够把所有的 Project 都编译出来呢?

答案是可以的。 在Gradle中,这叫Multi-Projects Build。需要在根目录下放一个 build.gradle 和一个 settings.gradle

可以看到 project/build.gradle 文件中的内容类似如下:

buildscript {
    //为当前项目配置仓库
    repositories {
        //jcenter 是一个函数,表示编译过程中依赖的库,
        //所需的插件可以在 jcenter 仓库中下载
        jcenter()
    }
    //定义编译脚本依赖的库
    dependencies {
         //表示我们编译的时候,依赖 Android 开发的 gradle 插件
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}
//为所有的子项目配置
allprojects {
    repositories {
        jcenter()
    }
}
// 项目 clean task
task clean(type: Delete) {
    delete rootProject.buildDir
}

这个 build.gradle 主要作用是配置其他子 Project 。比如,为子 Project 添加一些属性。这个 build.gradle 有没有都无所属。

project/settings.gradle 则主要定义了根目录下具体有多少个 Gradle Project ,其内容类似于:

include ':app', ':library-test'

这个文件很重要,名字必须是 settings.gradle。它里边用来告诉 Gradle,这个 Multi-Projects 包含多少个子 Project。

生命周期

当我们执行 Gradle 的时候,Gradle 首先是按顺序解析各个 Gradle 文件。这里边就有所所谓的生命周期的问题,即先解析谁,后解析谁。

Gradle 构建系统有自己的生命周期,初始化、配置和运行三个阶段。

img

  • 初始化阶段

    读取项目根目录中 setting.gradle 中的 include 信息,决定有哪几个工程加入构建,并为每个项目创建一个 Project 对象实例,比如下面有两个工程:

    include ':app', ':library-test'
    

    所以 Gradle 将会为它们两个分别创建一个 Project 对象实例。

  • 配置阶段

    执行所有 Project 中的 build.gradle 脚本,配置 Project 对象,一个对象由多个任务组成,此阶段也会去创建、配置 task 及相关信息。

  • 运行阶段

    根据 Gradle 命令传递过来的 task 名称,执行相关依赖任务。task 的执行阶段。首先执行 doFirst {} 闭包中的内容,最后执行 doLast {} 闭包中的内容。

Gradle 基于 Groovy,Groovy 又基于 Java。所以,Gradle 执行的时候和 Groovy 一样,会把脚本转换成 Java 对象。Gradle 主要有三种对象,这三种对象和三种不同的脚本文件对应,在 Gradle 执行的时候,会将脚本转换成对应的对端:

  • Gradle 对象

    当我们执行 gradle xxx 或者什么的时候,gradle 会从默认的配置脚本中构造出一个 Gradle 对象。在整个执行过程中,只有这么一个对象。Gradle 对象的数据类型就是 Gradle。我们一般很少去定制这个默认的配置脚本。

  • Project 对象

    每一个 build.gradle 会转换成一个 Project 对象。

  • Settings 对象

    显然,每一个 settings.gradle 都会转换成一个 Settings 对象。

对于其他 gradle 文件,除非定义了 class,否则会转换成一个实现了 Script 接口的对象。

Task

Task 是 Gradle 中的一种数据类型,它代表了一些要执行或者要干的工作。不同的插件可以添加不同的 Task。每一个 Task 都需要和一个 Project 关联。

Task 的 API 文档位于:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html。

  • 任务创建

    task hello {
        doLast {//doLast 可用 << 代替,不推荐此写法
            println "hello"//在 gradle 的运行阶段打印出来
        }
    }
    
    task hello {
        println "hello"//在 gradle 的配置阶段打印出来
    }
    

    task 中有一个 action list,task 运行时会顺序执行 action list 中的 action,doLast 或者 doFirst 后面跟的闭包就是一个 action,doLast 是把 action 插入到 list 的最后面,而 doFirst 是把 action 插入到 list 的最前面。

  • 任务依赖

    当我们在 Android 工程中执行 ./gradlew build 的时候,会有很多任务运行,因为 build 任务依赖了很多任务,要先执行依赖任务才能运行当前任务。

    任务依赖主要使用 dependsOn 方法,如下所示:

    task A << {println 'Hello from A'}
    task B << {println 'Hello from B'}
    task C << {println 'Hello from C'}
    B.dependsOn A	//执行 B 之前会先执行 A
    C.dependsOn B	//执行 C 之前会先执行 B
    

    另外,你也可以在 Task 的配置区中来声明它的依赖:

    task A << {println 'Hello from A'}
    task B {
        dependsOn A
        doLast {
            println 'Hello from B'  
        }
    }
    

    mustRunAfter:

    例如下面的场景,A 依赖 B,A 又同时依赖 C。但执行的结果可能是 B -> C -> A,我们想 C 在 B 之前执行,可以使用 mustRunAfter。

    task A << {println 'Hello from A'}
    task B << {println 'Hello from B'}
    task C << {println 'Hello from C'}
    A.dependsOn B
    A.dependsOn C
    B.mustRunAfter C	//B 必须在 C 之后执行
    

    finalizedBy:在 Task 执行完之后要执行的 Task。

  • 增量构建

    你在执行 Gradle 命令的时候,是不是经常看到有些任务后面跟着 [UP-TO-DATE],这是怎么回事?

    在 Gradle 中,每一个 Task 都有 inputs 和 outputs,如果在执行一个 Task时,如果它的输入和输出与前一次执行时没有发生变化,那么 Gradle 便会认为该 Task 是最新的,因此 Gradle 将不予执行,这就是增量构建的概念。

    一个 Task 的 inputs 和 outputs 可以是一个或多个文件,可以是文件夹,还可以是 project 的某个 property,甚至可以是某个闭包所定义的条件。自定义 Task 默认每次执行,但通过指定 inputs 和 outputs,可以达到增量构建的效果。

  • 依赖传递

    Gradle 默认支持传递性依赖,比如当前工程依赖包A,包 A 依赖包 B,那么当前工程会自动依赖包 B。同时,Gradle 支持排除和关闭依赖性传递。

    之前引入远程 AAR,一般会这样写:

    compile 'com.somepackage:LIBRARY_NAME:1.0.0@aar'
    

    上面的写法会关闭依赖性传递,所以有时候可能就会出问题,为什么呢?

    本来以为 @aar 是指定下载的格式,但其实不然,远程仓库文件下载格式应该是由 pom 文件中 packaging 属性决定的,@ 符号的真正作用是 Artifact only notation,也就是只下载文件本身,不下载依赖,相当于变相的关闭了依赖传递。

常用命令

$ gradle tasks // 查看根目录包含的 task

$ gradle tasks -all // 查看根目录包含的所有 task

$ gradle app:tasks //查看具体 Project 中的 task

$ gradle projects //查看项目下所有的子 Project

$ gradle build //构建项目

Android Studio 的 Terminal 中:

$ ./gradlew tasks // 查看根目录包含的 task

$ ./gradlew tasks -all // 查看根目录包含的所有 task

$ ./gradlew app:tasks //查看具体 Project 中的 task

$ ./gradlew projects //查看项目下所有的子 Project

$ ./gradlew build //构建项目

环境变量

Mac 中使用 Gradle 命令会出现 bash gradle command not found 原因是没有配置环境变量。

  • 找到 Gradle 所在的路径

    在 Finder 应用程序中 -> Android Studio 右键 -> 显示包内容,打开之后按照 Contents -> gradle -> gradle-xxx -> bin -> gradle。

    找到 gradle 文件后,右键 -> 显示简介,复制路径,类似:

    /Applications/Android\ Studio.app/Contents/gradle/gradle-4.4

  • 设置环境变量

    $ cd ~ //返回 Home 目录

    $ touch .base_profile //创建 base_profile 文件

    $ open -e .base_profile //使用文本编辑器打开 base_profile 文件

    输入以下内容:

    export GRADLE_HOME=/Applications/Android\ Studio.app/Contents/gradle/gradle-4.4
    export PATH=${PATH}:${GRADLE_HOME}/bin
    

    $ source .bash_profile //使修改生效

    $ gradle -v //显示 gradle 版本

    WARNING: An illegal reflective access operation has occurred
    WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/Applications/Android%20Studio.app/Contents/gradle/gradle-4.4/lib/groovy-all-2.4.12.jar) to method java.lang.Object.finalize()
    WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
    WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
    WARNING: All illegal access operations will be denied in a future release
    
    ------------------------------------------------------------
    Gradle 4.4
    ------------------------------------------------------------
    
    Build time:   2017-12-06 09:05:06 UTC
    Revision:     cf7821a6f79f8e2a598df21780e3ff7ce8db2b82
    
    Groovy:       2.4.12
    Ant:          Apache Ant(TM) version 1.9.9 compiled on February 2 2017
    JVM:          9.0.1 (Oracle Corporation 9.0.1+11)
    OS:           Mac OS X 10.13.3 x86_64
    

    如果显示如下问题,需要修改权限:

    -bash: /Applications/Android Studio.app/Contents/gradle/gradle-4.4/bin/gradle: Permission denied
    
  • 修改权限

    //进入 gradle 中 bin 目录

    $ cd /Applications/Android\ Studio.app/Contents/gradle/gradle-4.4/bin

    $ ls -l //查看权限

    total 24
    -rw-r--r--  1 jeanboy  admin  5286  4 16 17:49 gradle
    -rw-r--r--  1 jeanboy  admin  2250  4 16 17:49 gradle.bat
    

    $ chmod +x gradle //增加权限
    $ chmod +x gradle.bat //增加权限

    total 24
    -rwxr-xr-x  1 jeanboy  admin  5286  4 16 17:49 gradle
    -rwxr-xr-x  1 jeanboy  admin  2250  4 16 17:49 gradle.bat
    

更多文章

https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode

我的公众号

欢迎你「扫一扫」下面的二维码,关注我的公众号,可以接受最新的文章推送,有丰厚的抽奖活动和福利等着你哦!?

如果你有什么疑问或者问题,可以 点击这里 提交 issue,也可以发邮件给我 jeanboy@foxmail.com

同时欢迎你 Android技术进阶:386463747 来一起交流学习,群里有很多大牛和学习资料,相信一定能帮助到你!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值