Gradle 配置⽂件拆解
gradle 是什么
- 是构建⼯具,不是语⾔
- 它⽤了 Groovy 这个语⾔,创造了⼀种 DSL,但它本身不是语⾔
怎么构建?
按照 gradle 的规则(build.gradle、settings.gradle、gradle-wrapper、gradle 语法)
闭包
- Java 的 Lambda 表达式:是单抽象⽅法(SAM)的接⼝的匿名类对象的快捷写法,只是⼀个语法糖。
- Kotlin 的 Lambda 表达式:和匿名函数相当,实质上是⼀个函数类型的对象,并不只是语法糖。
- Groovy 的 Lambda 表达式:Groovy ⾥不叫「Lambda」表达式,⽽是叫「闭包」;在功能上,和Kotlin 的 Lambda ⽐较相似,都是⼀个「可以传递的代码块」,具体的功能⽐ Kotlin 的 Lambda更强⼀些,但基本的概念是⼀样的。
例如下边的实例,就是一个闭包:
js
plugins {
id 'com.android.application' version '8.2.0' apply false
id 'com.android.library' version '8.2.0' apply false
}
为什么 Groovy 可以写出类似 JSON 格式的配置?
- 因为它们其实都是⽅法调⽤,只是⽤闭包来写成了看起来像是JSON型的格式。
build.gradle分类:项目级 和模块级
项目级build.gradle
(顶级 build.gradle)
这是项目的根目录下的文件,主要负责配置项目的构建环境、插件和依赖等。通常会包含以下内容:
js
// 项目级 build.gradle 示例
buildscript {
repositories {
google() // 从 Google 的 Maven 仓库中获取依赖
mavenCentral() // 从 Maven Central 仓库获取依赖
}
dependencies { // classpath就是再上边的repositories内部的地址中获取的
classpath 'com.android.tools.build:gradle:7.0.4' // Android Gradle 插件版本
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21' // Kotlin 插件
}
}
allprojects {
repositories {
google() // 为所有模块提供 Google 的 Maven 仓库
mavenCentral() // 为所有模块提供 Maven Central 仓库
}
}
关键部分:
buildscript
: 配置构建脚本的依赖项(如 Android Gradle 插件、Kotlin 插件等)。allprojects
: 配置所有模块所用的仓库地址。classpath
: 通过repositories
内部的地址中获取的,因此repositories
必须配置。
2. 模块级build.gradle
(具体模块的 build.gradle)
这个文件通常位于每个模块(例如,app
模块)下,主要用于配置该模块的具体构建信息,包括应用的插件、依赖、构建类型等。
js
// 模块级 build.gradle 示例(例如:app模块)
apply plugin: 'com.android.application' // 应用插件
apply plugin: 'kotlin-android' // Kotlin 插件
android {
compileSdkVersion 30 // 编译 SDK 版本
defaultConfig {
applicationId "com.example.myapp" // 应用包名
minSdkVersion 21 // 最小 SDK 版本
targetSdkVersion 30 // 目标 SDK 版本
versionCode 1 // 应用版本号
versionName "1.0" // 应用版本名称
}
buildTypes {
release {
minifyEnabled false // 是否启用代码混淆
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // 混淆配置文件
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.6.0' // 依赖库
implementation 'com.google.android.material:material:1.4.0' // 依赖库
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.5.21' // Kotlin 标准库
}
3. 构建过程的工作流程
- 项目级
build.gradle
:这个文件主要用于为整个项目配置构建脚本和共享依赖,确保所有模块都能获得构建所需的插件和依赖。 - 模块级
build.gradle
:具体模块使用这个文件来配置应用的构建方式、依赖关系以及不同构建类型(如发布版和调试版)的设置。
4. 其他常见配置
gradle.properties
:可以在这个文件中定义全局的 Gradle 属性,通常用于配置项目或构建的常规设置。settings.gradle
:用于配置包含在项目中的模块。例如下边的代码:
js
rootProject.name = "Example"
include("app")
include("depend_main")
buildTypes, productFlavors和flavorDemension
1. buildTypes
buildTypes
用于定义构建过程中的不同类型(如 debug
和 release
),这些类型在构建时会有不同的配置。例如,debug
类型可能会启用调试工具,而 release
类型则通常会启用优化和混淆。
js
android {
buildTypes {
debug {
// 调试版本特定配置
applicationIdSuffix ".debug" // 为 debug 构建添加后缀
debuggable true // 开启调试功能
buildConfigField "boolean", "DEBUG", "true" // 在代码中使用 BuildConfig.DEBUG 来区分调试模式
}
release {
// 发布版本特定配置
minifyEnabled true // 启用代码混淆(ProGuard)
shrinkResources true // 压缩资源
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // ProGuard 配置文件
buildConfigField "boolean", "DEBUG", "false" // 在代码中使用 BuildConfig.DEBUG 来区分发布模式
}
}
}
常见的 buildTypes
配置:
- debug :通常启用调试信息、日志、调试工具等。通过
applicationIdSuffix
可以为调试版本添加后缀,避免和发布版本冲突。 - release :通常优化代码,启用混淆、压缩资源、签名等。
minifyEnabled
启用代码混淆,shrinkResources
启用资源压缩。 - 可以自定义更多的
buildTypes
,根据需要为不同构建类型提供不同的配置。
示例:
- debug 构建:通常启用调试功能,包含开发用的日志输出。
- release 构建:经过优化和混淆的构建,通常是发布到线上的版本。
2. productFlavors
productFlavors
用于定义不同的产品版本或变种。一个项目可以通过 productFlavors
创建多个不同的版本(如 free
和 paid
,或 demo
和 full
)。每个 productFlavor
可以有不同的资源、代码或配置。
js
android {
productFlavors {
free {
// 免费版的特定配置
applicationIdSuffix ".free" // 为免费版添加后缀
resValue "string", "app_name", "MyApp Free" // 设置不同的资源值
}
paid {
// 收费版的特定配置
resValue "string", "app_name", "MyApp Paid" // 设置不同的资源值
buildConfigField "boolean", "IS_PAID", "true" // 设置不同的 BuildConfig 值
}
}
}
productFlavors
的作用:
- 创建不同的产品版本:例如,为一个应用创建一个免费版和一个付费版。免费版可能会有广告,而付费版没有广告。
- 资源差异化 :每个
productFlavor
可以使用不同的资源,如图片、字符串、布局等。 - 代码差异化 :通过
BuildConfig
和条件编译,产品变种可以有不同的代码行为。
示例:
- 免费版(free) :例如,免费版可能会有广告。
- 付费版(paid) :付费版没有广告,可能还会提供额外的功能。
3. buildTypes 和 productFlavors 的组合
buildTypes
和 productFlavors
可以组合在一起,创建多个构建变种。例如,为每种产品类型提供不同的构建类型(如调试和发布)。Gradle 会根据 productFlavors
和 buildTypes
的组合生成多个构建变种。
假设有 2 个 productFlavors
和 2 个 buildTypes
,Gradle 将生成 4 种构建变种:
freeDebug
freeRelease
paidDebug
paidRelease
js
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
}
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
productFlavors {
free {
resValue "string", "app_name", "MyApp Free"
}
paid {
resValue "string", "app_name", "MyApp Paid"
buildConfigField "boolean", "IS_PAID", "true"
}
}
}
在这种配置下,Gradle 会生成以下构建变种:
freeDebug
: 免费版的调试版本。freeRelease
: 免费版的发布版本。paidDebug
: 付费版的调试版本。paidRelease
: 付费版的发布版本。
flavorDemension
flavorDimensions
是在 productFlavors
中使用的一个概念,它允许我们将多个 productFlavors
组织成不同的维度。这意味着我们可以根据不同的维度来创建组合,而不仅仅是基于单一的产品变种。通过 flavorDimensions
,可以实现在构建时选择不同的组合方式来支持更多灵活的产品版本。
为什么使用 flavorDimensions
?
在没有 flavorDimensions
的情况下,所有的 productFlavors
都属于同一个维度,这样会生成笛卡尔积的所有组合。例如,如果有 2 个 productFlavors
和 2 个 buildTypes
,Gradle 会为每个 productFlavor
和 buildType
组合生成构建变种。如果有多个维度,flavorDimensions
允许我们将 productFlavors
分类,从而得到更细粒度的控制。
在没有 flavorDimensions
的情况下,所有的 productFlavors
都属于同一个维度,这样会生成笛卡尔积的所有组合。例如,如果你有 2 个 productFlavors
和 2 个 buildTypes
,Gradle 会为每个 productFlavor
和 buildType
组合生成构建变种。如果你有多个维度,flavorDimensions
允许你将 productFlavors
分类,从而得到更细粒度的控制。如果需要更多的灵活性,可以使用 flavorDimensions
来分组 productFlavors
,并为每个 productFlavor
指定维度。
js
android {
flavorDimensions 'version', 'mode' // 定义两个维度
productFlavors {
free {
dimension 'version' // 指定属于 version 维度
// 免费版配置
}
paid {
dimension 'version' // 指定属于 version 维度
// 收费版配置
}
demo {
dimension 'mode' // 指定属于 mode 维度
// 演示版配置
}
full {
dimension 'mode' // 指定属于 mode 维度
// 完整版配置
}
}
buildTypes {
debug {
// 调试版配置
}
release {
// 发布版配置
}
}
}
在这个例子中,flavorDimensions
定义了两个维度:
version
:区分free
和paid
版本(免费版和付费版)。mode
:区分demo
和full
版本(演示版和完整版)。
通过这种方式,我们可以组合出更复杂的产品变种。例如,我们有以下维度:
version
:free
或paid
mode
:demo
或full
buildType
:debug
或release
我们用坐标线来进行比较形象的理解,flavor配合demension就是下边这样的,version可以理解成x坐标,mode可以理解成y坐标,其中version的取值有free和paid,而mode的取值有demo和full,他们的交叉点就是不同的打包风格。
再加上buidType,那么 Gradle 会生成以下构建变种:
freeDemoDebug
freeDemoRelease
freeFullDebug
freeFullRelease
paidDemoDebug
paidDemoRelease
paidFullDebug
paidFullRelease
每多一个维度,就会增加变体数量。变体数量取决于维度内部的方式有几种。有几种就在之前的变体总量上乘以维度内部的数量就行了。
优点
- 灵活性更高 :通过
flavorDimensions
,你可以在同一项目中创建更复杂的组合,支持更多的构建变种。例如,你可以轻松区分应用的不同版本和模式,而不必将所有变种都组合在一起。 - 代码和资源定制 :你可以为每个
productFlavor
和每个维度指定不同的代码、资源和配置,以满足不同产品版本的需求。 - 避免冗余组合:在多个维度中,你可以避免无意义的组合或多余的变种,提升构建效率。
一个示例,例如我们正在在做一个有广告和无广告的应用,并且它有免费版和付费版,还需要区分release和debug版本,我们可以按以下方式设置:
js
android {
flavorDimensions 'version', 'ads'
productFlavors {
free {
dimension 'version' // 指定版本维度为 free
}
paid {
dimension 'version' // 指定版本维度为 paid
}
withAds {
dimension 'ads' // 指定广告维度为 withAds
}
withoutAds {
dimension 'ads' // 指定广告维度为 withoutAds
}
}
buildTypes {
debug {
// 调试版配置
}
release {
// 发布版配置
}
}
// 个性化源文件的配置,我们可以根据不同的版本将这些文件存入不同的文件夹,然后打包的时候就可以做区分了。
sourceSets {
main {// 所有版本共用的类文件和资源文件路径
java.srcDirs = ['core/java']
kotlin.srcDirs = ['core/java']
resources.srcDirs = ['core/resources']
res.srcDirs = ['core/res']
res.srcDir(collectRes(i18n_string_res))
assets.srcDirs = ['core/assets']
manifest.srcFile 'core/AndroidManifest.xml'
aidl.srcDirs = ['aidl']
}
version {
java.srcDirs = ["main/src-version"] // 定制java或者kotlin文件路径
res.srcDirs = ['../res-version'] // 定制资源文件路径
}
paid {
java.srcDirs = ["main/src-paid"]
res.srcDirs '../res-product-default'
res.srcDirs '../paid/Paid/res-product-default'
}
withAds {
java.srcDirs = ["main/src-withAds"]
res.srcDirs '../res-ads-default'
res.srcDirs '../paid/withAds/res-product-default'
}
withoutAds {
java.srcDirs = ["main/src-withoutAds"]
res.srcDirs '../res-withoutads-default'
res.srcDirs '../paid/withoutAds/res-product-default'
}
}
}
此时,Gradle 会生成 8 个构建变种(2 个版本维度 × 2 个广告维度 × 2 个构建类型):
freeWithAdsDebug
freeWithAdsRelease
freeWithoutAdsDebug
freeWithoutAdsRelease
paidWithAdsDebug
paidWithAdsRelease
paidWithoutAdsDebug
paidWithoutAdsRelease
小结
flavorDimensions
允许我们将productFlavors
分组到不同的维度,从而创建更灵活的构建变种。- 每个
productFlavor
必须指定它属于哪个维度。 - 组合不同的维度和
productFlavors
会生成多个构建变种,我们可以根据需要定制资源、代码和构建配置。
总结
buildTypes
:定义构建的类型,如debug
和release
,用于设置调试或发布版本的行为。productFlavors
:定义不同的产品版本(如免费版和付费版),用于根据不同版本定制资源、代码和配置。flavorDimensions
允许我们将productFlavors
分组到不同的维度,从而创建更灵活的构建变种- 组合使用 :
buildTypes
,productFlavors
和flavorDimensions
可以组合使用,生成多个构建变种,每个变种具有不同的构建配置和行为。
compile, implementation 和 api
- implementation:不会传递依赖
- compile / api:会传递依赖;api 是 compile 的替代品,效果完全等同当依赖被传递时,⼆级依赖的改动会导致 0 级项⽬重新编译;
- 当依赖不传递时,⼆级依赖的改动不会导致 0 级项⽬重新编译
Gradle Wrapper
- 通过「只同步版本,不同步⽂件」的⽅式来减⼩协作项⽬的⼤⼩
- 每个⼈电脑上的 Gradle 存放在固定位置,然后使⽤ Gradle Wrapper 的配置来取⽤对应的版本就⾏
gradle task
使⽤⽅法: ./gradlew taskName
js
task taskName {
初始化代码
doFirst {
task 代码
}
doLast {
task 代码
}
}
doFirst() doLast() 和普通代码段的区别:
- 普通代码段:在 task 创建过程中就会被执⾏,发⽣在 configuration 阶段
- doFirst() 和 doLast():在 task 执⾏过程中被执⾏,发⽣在 execution 阶段。如果⽤户没有直接或间接执⾏ task,那么它的 doLast() doFirst() 代码不会被执⾏
- doFirst() 和 doLast() 都是 task 代码,其中 doFirst() 是往队列的前⾯插⼊代码,doLast() 是往队列的后⾯插⼊代码
task 的依赖: 可以使⽤ task taskA(dependsOn: b) 的形式来指定依赖。指定依赖后,task 会在⾃⼰执⾏前先执⾏⾃⼰依赖的 task。
gradle 执⾏的⽣命周期
Gradle 的构建过程是基于 任务(tasks) 的,任务是执行构建的最小单位。Gradle 在执行构建时,通常会遵循以下的生命周期阶段:
- 初始化阶段(Initialization Phase)
- 配置阶段(Configuration Phase)
- 执行阶段(Execution Phase)
1. 初始化阶段(Initialization Phase)
初始化阶段:执⾏ settings.gradle,确定主 project 和⼦ project。在这一阶段,Gradle 会确定哪些项目(Project)需要构建。如果在多项目构建中工作,Gradle 会评估并确定所有参与构建的项目。
- Gradle 会加载
settings.gradle
或settings.gradle.kts
文件,确定需要构建的项目模块。 - 该阶段的任务是确定哪个项目需要被构建,及其之间的依赖关系。
举例:
- 如果在多项目构建中,Gradle 会查找根项目以及子项目,建立项目之间的关系。
2. 配置阶段(Configuration Phase)
在配置阶段,Gradle会执⾏每个 project 的 bulid.gradle, 并按照结果评估和配置所有的 构建脚本 ,确定出所有 task,并在内存中针对这些task构建 有向无环图的任务图(task graph) 。
- Gradle 会执行每个项目中的
build.gradle
文件(或build.gradle.kts
,如果是 Kotlin DSL),并配置所有的任务。 - 在这一阶段,所有的任务都已经定义好了,但它们还没有执行。Gradle 主要完成任务的配置工作。
关键操作:
- 配置构建脚本:评估项目中的
build.gradle
文件。 - 确定所有任务的依赖关系:Gradle 会扫描每个任务,确定任务的执行顺序。
3. 执行阶段(Execution Phase)
执行阶段是真正开始执行任务的阶段,Gradle 会按照任务图中的顺序执行任务。
- 在此阶段,Gradle 会根据任务的依赖关系执行任务。
- 如果某个任务已经被标记为 up-to-date(已是最新状态,不需要执行),它会被跳过,以提高构建效率。
关键操作:
- 逐一执行任务:执行
build.gradle
中配置的所有任务。 - 如果有依赖关系,Gradle 会按照任务的依赖顺序执行。
Gradle 生命周期中的核心组成部分
1. 任务(Task)
任务是 Gradle 构建过程中最小的执行单元。每个任务都包含了一些具体的工作,例如编译代码、创建 JAR 文件、运行测试等。
- 每个任务都有 名称 和 执行的行为。
- 任务可以依赖其他任务,Gradle 会根据依赖关系自动确定执行顺序。
js
task hello {
doLast {
println 'Hello, Gradle!'
}
}
要单独执行上边的任务,可以执行 /.gradlew hello
.
2. 构建脚本(Build Script)
构建脚本是 Gradle 构建的核心。它通常位于项目的 build.gradle
或 build.gradle.kts
文件中,定义了任务、依赖和插件等。
3. 任务图(Task Graph)
任务图描述了任务之间的依赖关系。在配置阶段,Gradle 构建有向无关任务图,并在执行阶段根据这个图来执行任务。
Gradle 的内置生命周期任务
Gradle 提供了一些内置的生命周期任务,这些任务是 Gradle 构建生命周期中的一些关键节点,通常是以 阶段 为单位进行划分的。
初始化阶段的任务:
settings.gradle
文件的评估。
配置阶段的任务:
- 配置项目的
build.gradle
文件,创建任务和设置依赖。
执行阶段的任务:
build
:这是一个标准的生命周期任务,通常会触发编译、测试、打包等操作。clean
:清理构建的中间文件和输出文件,确保下次构建时不会受到之前构建结果的影响。assemble
:生成构建输出(如 JAR、APK 等)。check
:运行所有的检查任务(通常是测试任务)。test
:运行所有的单元测试。
Gradle 的默认生命周期
Gradle 的默认生命周期通常包括以下几个关键阶段和任务:
clean
:清理构建目录(删除生成的文件)。assemble
:将应用程序的构建输出打包成目标文件(例如 APK、JAR)。build
:执行包括测试、编译、打包等一系列任务,最终构建出完整的应用。
示例:Gradle 构建生命周期流程
-
初始化阶段:
- Gradle 加载
settings.gradle
,确定需要构建的项目模块。
- Gradle 加载
-
配置阶段:
- Gradle 执行各个
build.gradle
文件,配置任务和依赖。
- Gradle 执行各个
-
执行阶段:
- Gradle 执行
clean
任务:清理输出文件夹。 - 执行
build
任务:编译代码、执行单元测试、生成输出文件。
- Gradle 执行
任务的执行顺序
Gradle 会根据任务之间的依赖关系来确定执行顺序。任务的依赖关系可以是显式定义的,也可以是 Gradle 自动推导的。
例如:
js
task cleanBuild {
dependsOn clean, build
}
在上面的例子中,cleanBuild
任务依赖于 clean
和 build
,所以 Gradle 会先执行 clean
,然后执行 build
。
总结
Gradle 的执行生命周期分为三个主要阶段:
- 初始化阶段:确定哪些项目需要构建。
- 配置阶段:评估构建脚本并配置任务。
- 执行阶段:执行任务,完成构建过程。
Gradle 的生命周期非常灵活,任务可以根据依赖关系来串联,Gradle 会智能地决定任务的执行顺序。通过使用 build.gradle
配置文件,你可以定制构建过程,定义各种自定义任务,确保构建过程符合项目需求。
在gradle执行的生命周期内插入代码
在初始化阶段插入代码
在 Gradle 的 初始化阶段 (Initialization Phase),可以通过 gradle.beforeProject
或 gradle.projectsLoaded
等钩子来执行代码。
示例:在初始化阶段插入代码
js
gradle.beforeProject { project ->
println "Before evaluating project: ${project.name}"
}
gradle.projectsLoaded {
println "Projects have been loaded"
}
📌 解释:
gradle.beforeProject {}
:在每个build.gradle
被解析之前执行。gradle.projectsLoaded {}
:在所有项目被加载后执行(多项目构建时有用)。
2. 在配置阶段插入代码
在 配置阶段 (Configuration Phase),Gradle 解析 build.gradle
文件并创建任务,可以使用 gradle.afterProject
或 gradle.projectsEvaluated
来插入代码。
示例:在配置阶段插入代码
js
gradle.afterProject { project ->
println "After evaluating project: ${project.name}"
}
gradle.projectsEvaluated {
println "All projects evaluated"
}
📌 解释:
gradle.afterProject {}
:在每个build.gradle
解析完成后执行。gradle.projectsEvaluated {}
:所有项目的build.gradle
配置完毕后执行。
3. 在执行阶段插入代码
在 执行阶段(Execution Phase),Gradle 执行任务,可以在任务执行前后插入代码。
示例:在任务执行前后插入代码
js
tasks.whenTaskAdded { task ->
println "Task added: ${task.name}"
}
tasks.matching { it.name == 'build' }.all {
doFirst {
println "Before executing build task"
}
doLast {
println "After executing build task"
}
}
📌 解释:
tasks.whenTaskAdded {}
:当新任务被创建时执行。doFirst {}
:在任务执行前执行。doLast {}
:在任务执行后执行。
4. 在构建结束时插入代码
可以使用 gradle.buildFinished {}
在整个构建完成后插入代码。
示例:在构建结束后插入代码
js
gradle.buildFinished { result ->
if (result.failure) {
println "Build failed: ${result.failure}"
} else {
println "Build successful!"
}
}
📌 解释:
gradle.buildFinished {}
:在构建完成后执行,无论成功或失败。result.failure
:检查构建是否失败,并打印失败原因。
5. 完整示例
js
task myTask { // 添加了一个自定义的task
doLast {
println 'Hello, Gradle!'
}
}
gradle.beforeProject { project ->
println "Before evaluating project: ${project.name}"
}
gradle.afterProject { project ->
println "After evaluating project: ${project.name}"
}
gradle.projectsEvaluated {
println "All projects evaluated"
}
tasks.whenTaskAdded { task ->
println "Task added: ${task.name}"
}
tasks.matching { it.name == 'myTask' }.all { // 当然也可以对系统的默认task做插入
doFirst {
println "Before executing build task"
}
doLast {
println "After executing build task"
}
}
gradle.buildFinished { result ->
if (result.failure) {
println "Build failed: ${result.failure}"
} else {
println "Build successful!"
}
}
6. Gradle 生命周期钩子总结
生命周期阶段 | 钩子 | 作用 |
---|---|---|
初始化阶段 | gradle.beforeProject {} |
在项目 build.gradle 解析前执行 |
初始化阶段 | gradle.projectsLoaded {} |
在所有项目加载完成后执行 |
配置阶段 | gradle.afterProject {} |
在 build.gradle 解析完成后执行 |
配置阶段 | gradle.projectsEvaluated {} |
在所有 build.gradle 解析完成后执行 |
执行阶段 | tasks.whenTaskAdded {} |
在某个任务被创建时执行 |
执行阶段 | task.doFirst {} |
在某个任务执行前执行 |
执行阶段 | task.doLast {} |
在某个任务执行后执行 |
构建完成 | gradle.buildFinished {} |
在构建完成后执行 |
7. 特别说明 afterEvaluate
afterEvaluate {}
是 Project 级别的生命周期回调方法,针对的事单个模块,如果应用只有一个模块,那就是针对整个应用了,它在 Gradle 配置阶段(Configuration Phase)结束后 触发,但在执行阶段(Execution Phase)之前执行。
作用:
- 允许在所有
build.gradle
文件 解析完成后 再修改Project
的配置,例如动态添加任务或修改已有任务的配置。 afterEvaluate {}
适用于在build.gradle
解析完成后,对任务、依赖或构建逻辑进行 额外修改。- 适用于插件开发,或者当需要修改 Gradle 配置但无法直接在
build.gradle
中修改时使用。
afterEvaluate {}
的基本用法
js
project.afterEvaluate {
println "项目配置完成,现在可以修改任务了!"
}
📌 执行时机:
- 当
build.gradle
配置完成 ,但 任务尚未开始执行 时触发。
使用场景
1. 在任务定义后修改任务
在 build.gradle
配置完成后,修改某个任务的配置 。例如,给 assembleDebug
任务添加一个 doLast {}
:
javascript
groovy
复制编辑
afterEvaluate {
tasks.named("assembleDebug") {
doLast {
println "assembleDebug 任务完成后执行"
}
}
}
📌 适用情况:
- 某些任务在
build.gradle
配置阶段尚未创建,afterEvaluate {}
可用于在这些任务被创建后再修改它们。
2. 在插件应用后修改插件行为
如果一个插件会在 build.gradle
解析后添加任务,我们可以使用 afterEvaluate {}
在插件完成初始化后 修改其任务。例如:
js
apply plugin: 'com.android.application'
afterEvaluate {
android.applicationVariants.all { variant ->
println "Variant: ${variant.name}"
}
}
📌 适用情况:
- 需要在 Android Gradle Plugin 解析完成后,对
applicationVariants
进行操作(例如修改打包方式、动态添加任务等)。
3. 动态添加任务
使用 afterEvaluate {}
在所有项目配置完成后 动态创建任务。
js
afterEvaluate {
tasks.register("customTask") {
doLast {
println "这是 afterEvaluate 添加的任务"
}
}
}
📌 适用情况:
- 如果一个任务需要基于
build.gradle
解析后的信息(如依赖项、插件等)才能创建。
4. 确保 ext {}
变量已解析
有时候 ext {}
变量可能在 Gradle 配置阶段 结束前未解析完全,导致访问时报错。使用 afterEvaluate {}
可以确保变量已经解析:
js
ext {
myVersion = "1.0.0"
}
afterEvaluate {
println "myVersion = ${myVersion}"
}
📌 适用情况:
- 需要确保
ext {}
变量 在配置阶段结束后 正确解析。
afterEvaluate {}
vs. gradle.projectsEvaluated {}
方法 | 作用 | 触发时机 |
---|---|---|
afterEvaluate {} |
适用于 单个项目 ,在该项目的 build.gradle 解析完成后 执行 |
每个项目的 build.gradle 解析完成后 执行 |
gradle.projectsEvaluated {} |
适用于 整个 Gradle 构建 ,当 所有 项目的 build.gradle 配置完成后执行 |
所有 build.gradle 解析完成后,执行一次 |
总结
✅ afterEvaluate {}
适用于:
- 在 任务创建后 但 任务执行前 进行修改。
- 在 插件完成初始化 后,修改插件的行为。
- 在
build.gradle
配置完成后 动态添加任务 或 修改已存在的任务。 - 确保
ext {}
变量、applicationVariants
及 Gradle 插件的配置完成。
示例(完整应用) :
js
apply plugin: 'com.android.application'
afterEvaluate {
println "项目配置完成!"
tasks.named("assembleDebug") {
doLast {
println "assembleDebug 任务执行后执行"
}
}
tasks.register("customTask") {
doLast {
println "这是 afterEvaluate 添加的任务"
}
}
}
🚀 总结一句话:
afterEvaluate {}
适用于 在 Gradle 配置阶段结束后 但 任务执行前 进行修改任务或动态调整构建逻辑。
8. 结论
- 初始化阶段 可以用
gradle.beforeProject
和gradle.projectsLoaded
来插入代码。 - 配置阶段 可以用
gradle.afterProject
和gradle.projectsEvaluated
来插入代码。 - 执行阶段 可以用
tasks.whenTaskAdded
、doFirst
和doLast
来插入代码。 - 构建完成后 可以用
gradle.buildFinished
处理成功或失败的情况。