Skip to content

第28讲:AndroidGradle构建问题解析 ​

想必我们做 Android App 开发的应该对 gradle 都不太陌生,因为 Android Studio 的帮助,Android 工程师使用 gradle 的门槛不算太高,基本的配置都大同小异,只要在 Android Studio 默认生成的 build.gradle 中稍加修改,都能满足项目要求。但是深入细致的了解 gradle 的基本知识,还是能够帮助我们更优雅地实现项目的配置工作。有些场景 gradle 甚至能帮助我们完成一些业务上的需要。这节课我们就来了解一下 gradle 那些需要掌握的基本知识。

gradle Task ​

Task(任务)可以理解为 gradle 的执行单元,gradle 通过执行一个个 Task 来完成整个项目构建工作。

自定义 Task ​

我们可以在 build.gradle 中使用关键字 task 来自定义一个 Task。比如创建 build.gradle 文件,并添加 task,如图所示:

上图中定义了一个简单的 Task A,然后在终端中使用以下命令行执行此 Task,即可看到打印结果,如图所示:

从结果中可以看出,打印日志是在 gradle 的配置(Configure)阶段执行的。gradle 的构建生命周期包含 3 部分:初始化阶段、配置阶段、执行阶段。在 task A 中添加 doFirst 闭包,如下所示:

再次执行 gradle A,打印结果如下所示:

gradle 在运行期会执行所有 task 的配置语句,然后执行指定的 Task。

Task 之间可以存在依赖关系 ​

gradle 中的 Task 可以通过 dependsOn 来指定它依赖另一个 Task,如下所示:

在 build.gradle 中,我新加了一个 Task B,并通过 dependsOn 关键字指定 task B 依赖于 task A。 在命令行中执行"gradle B",结果如下所示:

可以看出虽然我们只是执行了 task B,但是因为依赖关系的存在,task A 也会被执行。

gradle 会在配置 Configure 阶段,确定依赖关系。对于 Android 项目来说即为执行各个 module 下的 build.gradle 文件,这样各个 build.gradle 文件中的 task 的依赖关系就被确认下来了,而这个依赖关系的确定就是在 Configuration 阶段。

gradle 自定义方法 ​

我们可以在 build.gradle 中使用 def 关键字,自定义方法,比如以下代码中自定义了 getDate 方法,并在 task 中使用此方法。

通过 gradle 命令执行上述 task,结果如下:

系统预置 task ​

自定义 task 时,还可以使用系统提供的各种显示 task 来完成相应的任务。具体就是使用关键字 type 来指定使用的是哪一个 task。

比如我在当前目录下新建 2 个文件夹:src 和 dst。目录如下:

然后在 src 中创建文件 Demo.java,代码如下

最后当前路径结构如下:

修改 build.gradle,新添加一个 task copy 如下:

然后在命令行中执行"gradle copy",运行结束后,重新查看当前目录结构如下:

可以看出 Demo.java 被拷贝了一份到 dst 目录中。

除了 Copy 之外,还有很多其他显示的 task 可用,比如我们可以通过自定义 task 实现编译 Demo.java 并将编译后的 .class 输出到某一特定路径,具体实现如下所示:

解释说明:

  1. 通过 type: JavaCompile 指定是编译 Java 类的 task;

  2. source 指定需要编译类的文件路径;

  3. include 指定需要编译哪一个 Java 类;

  4. destinationDir 指定编译之后,生成 .class 文件的保存路径。

最后命令行中执行"gradle compile",查看目录结构如下:

gradle project ​

在 Android 中每个 module 就对应着一个 project,gradle 在编译时期会为每一个 project 创建一个 Project 对象用来构建项目。这一过程是在初始化阶段,通过解析 settings.gradle 中的配置来创建相应的 Project。

上图 settings.gradle 中导入了 3 个 project,但是实际上还会有一个根 project,使用 ./gradlew project 查看,如下所示:

我们可以在根 project 中统筹管理所有的子 project,具体在 LagouGradle 路径下的 build.gradle 中进行设置,如下所示:

这样写的好处是项目中所有 module 的配置都统一写在一个地方,统筹管理。比如经常会在主项目的 build.gradle 中添加包过滤,解决依赖冲突,如下所示:

buildSrc 统筹依赖管理 ​

随着项目越来越大,工程中的 module 越来越多,依赖的三方库也越来越多。一般情况下我们会在一个集中的地方统一管理这些三方库的版本。比如像谷歌官方推荐的使用 ext 变量,在根 module 的 build.gradle 中,使用 ext 集中声明各种三方库的版本,如下所示:

然后在子 module 中,引用这些版本信息。

但是这种写法有点小瑕疵:不支持 AS 的自动补充功能,也无法使用代码自动跟踪,因此可以考虑使用 buildSrc。

buildSrc 是 Android 项目中一个比较特殊的 project,在 buildSrc 中可以编写 Groovy 语言,但是现在谷歌越来也推荐使用 Kotlin 来编写编译语句。

先在根路径下创建目录 buildSrc,结构如下:

注意:这个工程的只能有一个,并且名字必须为 buildSrc。

创建好之后,在 buildSrc 中创建 build.gradle.kts 文件,并添加 Kotlin 插件。

编译工程有可能会报错,如下所示:

只要添加 repositories { jcenter() } 仓库即可。

接下来在 buildSrc 中创建 src/main/java 目录,并在此目录下创建 Dependencies.kt(名字可随意取)。在 Dependencies.kt 中创建两个 object,分别用来管理工程中的版本信息以及依赖库。

我们可以在 Versions 中添加各种项目中可能会引用到的版本。

然后在 Deps 中引用 Versions 中的变量。

最后我们就可以在各个 module 中的 build.gradle 中直接使用 Deps 中的变量用来声明依赖,比如在 app module 的 build.gradle 中添加如下依赖。

上图中分别是使用 buildSrc 前后的对比,并且在使用 Deps 的过程中,studio 会给出自动提示,如下:

总结 ​

这节课主要介绍了 gradle 构建中的 task 和 project。

task 与大部分的开发者开发是最为紧密的,它是 gradle 构建的基本单元。我们每次编译工程时,Android studio 会在控制台打印出执行的 task 名称,类似下图中的格式。

我们也可以自定义 task 实现相同的构建需求。

project 对应的项目中的 module,每个 module 中包含一个 build.gradle,每个 build.gradle 都会被 gradle 编译成 Project 字节码。我们在 build.gradle 中所写的所有逻辑,实际上最终都会被映射成此 Project 字节码内的实现逻辑。