💡
前面我们深入探讨了 Gradle 生命周期,理解了构建的 “运作机制”。今天,我们要更进一步,学习 Gradle 的 “语言”—— Gradle 基础语法与 DSL
你可能会觉得 Gradle 文件看起来有点 “神秘”,不像 Java 或 Kotlin 那样 “规规矩矩”。 这是因为 Gradle 构建脚本是使用 Groovy DSL (Domain Specific Language,领域特定语言) 编写的。
💡
Groovy 是一种基于 JVM 的动态语言,而 Gradle DSL 则是 Groovy 的一个 “方言”,专门为构建自动化而设计。
掌握 Gradle 基础语法和 Groovy DSL,就像学习一门新的编程语言,虽然一开始可能会有些陌生,但一旦入门,你就会发现它简洁、强大、灵活,能让你更高效地编写构建脚本,实现各种复杂的构建需求。

Gradle 构建脚本:Groovy DSL 的舞台

首先,我们要明确一点:Gradle 构建脚本 (例如 build.gradle, settings.gradle) 都是用 Groovy DSL 编写的。这意味着,要编写 Gradle 构建脚本,我们就需要了解 Groovy DSL 的基本语法。
Groovy DSL 构建在 Groovy 语言之上,它在 Groovy 的基础上,添加了一些专门为构建自动化设计的语法糖和约定,让构建脚本更简洁、更易读。但其本质仍然是 Groovy 代码。

Groovy 基础语法速览:Gradle DSL 的基石

虽然 Gradle DSL 在 Groovy 基础上做了简化和定制,但掌握一些 Groovy 基础语法,对于理解 Gradle DSL 仍然至关重要。下面,我们快速浏览一些 Groovy 常用语法元素:

语句和表达式 (Statements and Expressions)

  • 语句 (Statements): 执行某些操作的代码单元,例如变量声明、赋值、方法调用等。 语句通常以分号 ; 结尾 (但 Groovy 中分号是可选的)。
    • 表达式 (Expressions): 产生值的代码单元,例如字面量、变量、运算表达式、方法调用 (如果方法返回值) 等。 表达式可以作为语句的一部分,也可以单独存在。
      • Groovy 中,很多语句也可以作为表达式使用,例如赋值语句:

    变量 (Variables)

    • 动态类型 (Dynamic Typing): Groovy 是一种动态类型语言,变量声明时不需要显式指定类型,类型在运行时确定。 可以使用 def 关键字声明变量。
      • 类型可选: 即使不使用 def 关键字,也可以声明变量 (但建议使用 def 增加代码可读性)。
        • 变量作用域: Groovy 变量的作用域与其他编程语言类似,有局部变量、成员变量、全局变量等。在 Gradle 构建脚本中,我们通常使用局部变量和 Project 对象的属性。

        字符串 (Strings)

        Groovy 支持多种字符串表示方式:
        • 单引号字符串 ('...'): 最简单的字符串,不支持字符串插值 (String Interpolation)
          • 双引号字符串 ("..."): 支持 字符串插值 (String Interpolation),可以使用 ${变量名}${表达式} 在字符串中引用变量或表达式的值。
            • GString (Groovy String): 双引号字符串的另一种叫法,强调其支持字符串插值的特性。
            • 三单引号字符串 ('''...'''): 多行字符串,保留字符串中的换行和格式,不支持字符串插值
              • 三双引号字符串 ("""..."""): 多行字符串,保留字符串中的换行和格式,支持字符串插值

                列表 (Lists)

                • 创建列表: 使用方括号 [...] 创建列表,元素之间用逗号 , 分隔。 Groovy 列表可以是异构的,即可以包含不同类型的元素。
                  • 访问列表元素: 使用索引 (从 0 开始) 访问列表元素。
                    • 列表常用方法: Groovy 列表提供了很多便捷的方法,例如 add(), remove(), size(), each(), collect(), findAll() 等。

                      Map (Maps)

                      • 创建 Map: 使用方括号 [:]as 关键字创建 Map,键值对之间用冒号 : 分隔,键值对之间用逗号 , 分隔。
                        • 访问 Map 元素: 使用键名访问 Map 元素,可以使用点号 . 或方括号 [] 访问。
                          • Map 常用方法: Groovy Map 也提供了很多便捷的方法,例如 put(), remove(), size(), each(), keySet(), values() 等。

                            闭包 (Closures)

                            闭包 (Closures) 是 Groovy 最重要的特性之一,也是 Gradle DSL 的核心组成部分。 理解闭包对于编写 Gradle 构建脚本至关重要。
                            • 什么是闭包: 闭包是一段 代码块,可以像对象一样传递和执行。 它可以访问定义它的作用域中的变量 (即使在闭包定义的作用域之外执行)。 你可以把闭包理解为 匿名函数Lambda 表达式
                            • 闭包定义: 使用花括号 {} 定义闭包。
                              • 闭包执行: 使用 ()call() 方法执行闭包。
                                • 闭包参数: 闭包可以接收参数,参数定义在 > 符号之前。 如果没有显式声明参数,闭包会有一个默认参数 it
                                  • 闭包返回值: 闭包可以返回值,默认返回闭包中最后一条语句的值。 也可以使用 return 关键字显式返回值。
                                    • 闭包在 Gradle DSL 中的应用: Gradle DSL 大量使用了闭包来进行配置和任务定义。 例如,android {}, dependencies {}, task {} 等代码块,实际上都是 接受闭包作为参数的方法调用。 这些闭包被称为 配置闭包 (Configuration Closures)Action 闭包 (Action Closures),用于配置 Gradle 对象或定义 Task 的行为。

                                      方法 (Methods)

                                      • 方法定义: Groovy 方法定义与 Java 类似,但 返回类型是可选的 (Groovy 是动态类型语言)。 可以使用 def 关键字定义方法 (推荐)。
                                        • 方法调用: Groovy 方法调用非常灵活,可以省略括号 () (如果不会引起歧义)。
                                          • 方法在 Gradle DSL 中的应用: Gradle DSL 中,很多配置项都是通过 方法调用 来设置的。 例如,compileSdk 33, implementation 'androidx.core:core-ktx:1.9.0', google(), mavenCentral() 等,都是方法调用 (省略了括号)。

                                          注释 (Comments)

                                          Groovy 支持两种注释方式:
                                          • 单行注释: 使用 // 开头,注释到行尾。
                                            • 多行注释: 使用 /* ... */ 包围,可以跨越多行。

                                              Groovy DSL for Gradle:构建自动化的 “专属语言”

                                              理解了 Groovy 基础语法,我们再来看看 Gradle DSL 的特性,以及如何在 build.gradle 文件中使用 Groovy DSL。
                                              • DSL (Domain Specific Language): 领域特定语言,是为特定领域设计的编程语言,通常比通用编程语言更简洁、更易读、更易用。Gradle DSL 就是为构建自动化领域设计的 DSL。
                                              • 基于 Groovy: Gradle DSL 构建在 Groovy 语言之上,继承了 Groovy 的动态性、灵活性和简洁性。
                                              • 声明式风格 (Declarative Style): Gradle DSL 倾向于 声明式风格,即 描述 “要什么 (what to do)”,而不是 “怎么做 (how to do)”。例如,在 dependencies {} 代码块中,我们声明了项目需要的依赖,而不需要关心 Gradle 如何下载和管理这些依赖。 Gradle 会自动处理这些细节。
                                              • 约定优于配置 (Convention over Configuration): Gradle DSL 遵循 约定优于配置 的原则,提供了一些默认约定,例如默认的源代码目录结构 (src/main/java, src/main/kotlin),默认的构建产物目录 (build/outputs) 等。我们可以通过配置来覆盖这些约定,但通常情况下,使用默认约定可以简化构建脚本。
                                              • 代码块 (Blocks): Gradle DSL 使用 代码块 (Blocks) 来组织配置和定义构建逻辑。 例如,plugins {}, android {}, dependencies {}, tasks {} 等都是代码块,它们实际上都是 接受闭包作为参数的方法调用。代码块内部可以配置属性、调用方法、定义 Task 等。

                                              常用 Gradle DSL 代码块详解

                                              下面,我们详细讲解 build.gradle 文件中常用的 Gradle DSL 代码块:
                                              • plugins {} 代码块: 用于 声明和应用插件id 'plugin-id' 用于声明插件 ID,version 'plugin-version' 用于声明插件版本,apply false/true 用于控制是否立即应用插件 (Project-level build.gradle 通常使用 apply false,Module-level build.gradle 通常使用 apply true 或省略 apply)。
                                                • android {} 代码块: 由 Android Gradle 插件提供,用于 配置 Android 构建。 可以在 android {} 代码块中配置 compileSdk, defaultConfig {}, buildTypes {}, productFlavors {}, signingConfigs {}, compileOptions {}, kotlinOptions {}, buildFeatures {}, sourceSets {}, packagingOptions {}, lint {} 等 Android 构建相关的配置。
                                                  • dependencies {} 代码块: 用于 声明项目依赖。 可以在 dependencies {} 代码块中使用 implementation, api, compileOnly, runtimeOnly, testImplementation, androidTestImplementation, kapt, annotationProcessor 等依赖声明关键字,声明对远程仓库依赖、本地库依赖、模块依赖等。
                                                    • repositories {} 代码块: 用于 配置依赖仓库。 可以在 repositories {} 代码块中使用 google(), mavenCentral(), gradlePluginPortal(), maven { url 'repo-url' }, flatDir { dirs 'libs' } 等仓库声明关键字,配置 Maven 仓库、Google Maven 仓库、Gradle 插件仓库、本地仓库等。
                                                      • tasks {} 代码块 (或 tasks.register(...)) : 用于 定义和配置 Task。 可以使用 tasks.register('taskName') { ... }task taskName { ... } (旧语法,不推荐) 来注册 Task,并在代码块中配置 Task 的属性和行为。 可以使用 doFirst {}doLast {} 代码块来定义 Task 的执行逻辑 (Action)。

                                                        属性和方法 (Properties and Methods)

                                                        在 Gradle DSL 中,我们可以通过 属性 (Properties)方法 (Methods) 来配置和访问 Gradle 对象。
                                                        • 属性 (Properties): Gradle 对象暴露的配置项,可以直接赋值或读取。 例如,android.compileSdk = 33, defaultConfig.applicationId = "com.example.myapp", buildTypes.release.minifyEnabled = true 等。
                                                        • 方法 (Methods): Gradle 对象提供的方法,可以执行某些操作或返回某些值。 例如,getDefaultProguardFile('proguard-android-optimize.txt'), repositories.google(), dependencies.implementation(...), tasks.register(...), doFirst { ... }, doLast { ... } 等。
                                                        • 链式调用 (Chaining): Gradle DSL 支持链式调用,可以连续调用多个方法或访问多个属性,使代码更简洁。 例如,android.defaultConfig.proguardFiles(...)
                                                        • 动态方法 (Dynamic Methods): 由于 Groovy 的动态性,Gradle DSL 允许动态地调用一些方法,例如 Android Gradle 插件提供的 buildTypes.<buildType> {}, productFlavors.<flavorName> {} 等。 这些方法实际上是动态生成的,用于配置不同的 Build Type 和 Product Flavor。

                                                        访问 Project 属性

                                                        在 Gradle 构建脚本中,我们可以通过 project 对象访问当前 Project 的属性和方法。 常用的 Project 属性和方法包括:
                                                        • project.name: 获取项目名称。
                                                        • project.version: 获取项目版本号 (通常在 gradle.properties 中定义)。
                                                        • project.group: 获取项目 Group ID (通常在 gradle.properties 中定义)。
                                                        • project.buildDir: 获取构建输出目录 (build 目录)。
                                                        • project.rootDir: 获取项目根目录。
                                                        • project.file(...), project.files(...), project.fileTree(...): 用于创建 File, FileCollection, FileTree 对象,方便操作文件和目录。
                                                        • project.property(...): 获取在 gradle.properties 文件或命令行中定义的项目属性。
                                                        • project.tasks: 访问项目中的所有 Task。
                                                        示例:

                                                        Gradle Kotlin DSL (build.gradle.kts):类型安全的 “新选择”

                                                        虽然 Groovy DSL 是 Gradle 的 “传统语言”,但 Gradle 也提供了 Kotlin DSL 作为替代选择。 Kotlin DSL 使用 Kotlin 语言编写构建脚本,文件后缀为 .gradle.kts

                                                        Kotlin DSL 的优势:

                                                        • 类型安全 (Type Safety): Kotlin 是一种静态类型语言,Kotlin DSL 构建脚本具有类型安全,可以在编译时发现类型错误,提高代码质量和可靠性。
                                                        • 更好的 IDE 支持: Kotlin DSL 可以享受 Kotlin 语言强大的 IDE 支持,例如代码自动完成、代码导航、重构、错误检查等,提高开发效率。
                                                        • 更强的可读性和维护性: Kotlin DSL 语法更严谨、更结构化,代码可读性和维护性更高,尤其是在大型项目中。
                                                        • 与 Android 开发语言一致: 如果你的 Android 项目主要使用 Kotlin 语言开发,使用 Kotlin DSL 可以保持技术栈的一致性。

                                                        Kotlin DSL 示例 (build.gradle.kts):

                                                        选择 Groovy DSL 还是 Kotlin DSL?

                                                        • Groovy DSL: 成熟稳定,社区资源丰富,学习曲线相对平缓,对于小型项目或快速原型开发可能更方便。
                                                        • Kotlin DSL: 类型安全,IDE 支持更好,可读性和维护性更高,更适合大型项目和长期维护的项目,是 Gradle 官方推荐的未来方向。

                                                        最后:

                                                        • 掌握 Gradle 基础语法和 Groovy DSL,是成为 Gradle 高手的必经之路。
                                                        • 通过学习 Groovy 基础语法,理解 Gradle DSL 的特性和常用代码块,就能编写出更简洁、更强大、更灵活的 Gradle 构建脚本,更好地掌控 Android 构建流程。
                                                        • 在未来的博客中,我也会继续分享更多关于 Gradle DSL 的进阶用法和最佳实践,希望能与大家共同进步!
                                                        Gradle系列——依赖管理与插件机制Gradle系列——生命周期详解,理论结合实战
                                                        Loading...