Android Gradle 配置文件

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 用于定义构建过程中的不同类型(如 debugrelease),这些类型在构建时会有不同的配置。例如,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 创建多个不同的版本(如 freepaid,或 demofull)。每个 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 的组合

buildTypesproductFlavors 可以组合在一起,创建多个构建变种。例如,为每种产品类型提供不同的构建类型(如调试和发布)。Gradle 会根据 productFlavorsbuildTypes 的组合生成多个构建变种。

假设有 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 会为每个 productFlavorbuildType 组合生成构建变种。如果有多个维度,flavorDimensions 允许我们将 productFlavors 分类,从而得到更细粒度的控制。

在没有 flavorDimensions 的情况下,所有的 productFlavors 都属于同一个维度,这样会生成笛卡尔积的所有组合。例如,如果你有 2 个 productFlavors 和 2 个 buildTypes,Gradle 会为每个 productFlavorbuildType 组合生成构建变种。如果你有多个维度,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 :区分 freepaid 版本(免费版和付费版)。
  • mode :区分 demofull 版本(演示版和完整版)。

通过这种方式,我们可以组合出更复杂的产品变种。例如,我们有以下维度:

  • version: freepaid
  • mode: demofull
  • buildType: debugrelease

我们用坐标线来进行比较形象的理解,flavor配合demension就是下边这样的,version可以理解成x坐标,mode可以理解成y坐标,其中version的取值有free和paid,而mode的取值有demo和full,他们的交叉点就是不同的打包风格。

再加上buidType,那么 Gradle 会生成以下构建变种:

  • freeDemoDebug
  • freeDemoRelease
  • freeFullDebug
  • freeFullRelease
  • paidDemoDebug
  • paidDemoRelease
  • paidFullDebug
  • paidFullRelease

每多一个维度,就会增加变体数量。变体数量取决于维度内部的方式有几种。有几种就在之前的变体总量上乘以维度内部的数量就行了。

优点
  1. 灵活性更高 :通过 flavorDimensions,你可以在同一项目中创建更复杂的组合,支持更多的构建变种。例如,你可以轻松区分应用的不同版本和模式,而不必将所有变种都组合在一起。
  2. 代码和资源定制 :你可以为每个 productFlavor 和每个维度指定不同的代码、资源和配置,以满足不同产品版本的需求。
  3. 避免冗余组合:在多个维度中,你可以避免无意义的组合或多余的变种,提升构建效率。

一个示例,例如我们正在在做一个有广告和无广告的应用,并且它有免费版和付费版,还需要区分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 :定义构建的类型,如 debugrelease,用于设置调试或发布版本的行为。
  • productFlavors:定义不同的产品版本(如免费版和付费版),用于根据不同版本定制资源、代码和配置。
  • flavorDimensions 允许我们将 productFlavors 分组到不同的维度,从而创建更灵活的构建变种
  • 组合使用buildTypes , productFlavorsflavorDimensions 可以组合使用,生成多个构建变种,每个变种具有不同的构建配置和行为。

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 在执行构建时,通常会遵循以下的生命周期阶段:

  1. 初始化阶段(Initialization Phase)
  2. 配置阶段(Configuration Phase)
  3. 执行阶段(Execution Phase)

1. 初始化阶段(Initialization Phase)

初始化阶段:执⾏ settings.gradle,确定主 project 和⼦ project。在这一阶段,Gradle 会确定哪些项目(Project)需要构建。如果在多项目构建中工作,Gradle 会评估并确定所有参与构建的项目。

  • Gradle 会加载 settings.gradlesettings.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.gradlebuild.gradle.kts 文件中,定义了任务、依赖和插件等。

3. 任务图(Task Graph)

任务图描述了任务之间的依赖关系。在配置阶段,Gradle 构建有向无关任务图,并在执行阶段根据这个图来执行任务。

Gradle 的内置生命周期任务

Gradle 提供了一些内置的生命周期任务,这些任务是 Gradle 构建生命周期中的一些关键节点,通常是以 阶段 为单位进行划分的。

初始化阶段的任务:
  • settings.gradle 文件的评估。
配置阶段的任务:
  • 配置项目的 build.gradle 文件,创建任务和设置依赖。
执行阶段的任务:
  • build:这是一个标准的生命周期任务,通常会触发编译、测试、打包等操作。
  • clean:清理构建的中间文件和输出文件,确保下次构建时不会受到之前构建结果的影响。
  • assemble:生成构建输出(如 JAR、APK 等)。
  • check:运行所有的检查任务(通常是测试任务)。
  • test:运行所有的单元测试。

Gradle 的默认生命周期

Gradle 的默认生命周期通常包括以下几个关键阶段和任务:

  1. clean:清理构建目录(删除生成的文件)。
  2. assemble:将应用程序的构建输出打包成目标文件(例如 APK、JAR)。
  3. build:执行包括测试、编译、打包等一系列任务,最终构建出完整的应用。

示例:Gradle 构建生命周期流程

  1. 初始化阶段

    • Gradle 加载 settings.gradle,确定需要构建的项目模块。
  2. 配置阶段

    • Gradle 执行各个 build.gradle 文件,配置任务和依赖。
  3. 执行阶段

    • Gradle 执行 clean 任务:清理输出文件夹。
    • 执行 build 任务:编译代码、执行单元测试、生成输出文件。

任务的执行顺序

Gradle 会根据任务之间的依赖关系来确定执行顺序。任务的依赖关系可以是显式定义的,也可以是 Gradle 自动推导的。

例如:

js 复制代码
task cleanBuild {
    dependsOn clean, build
}

在上面的例子中,cleanBuild 任务依赖于 cleanbuild,所以 Gradle 会先执行 clean,然后执行 build

总结

Gradle 的执行生命周期分为三个主要阶段:

  1. 初始化阶段:确定哪些项目需要构建。
  2. 配置阶段:评估构建脚本并配置任务。
  3. 执行阶段:执行任务,完成构建过程。

Gradle 的生命周期非常灵活,任务可以根据依赖关系来串联,Gradle 会智能地决定任务的执行顺序。通过使用 build.gradle 配置文件,你可以定制构建过程,定义各种自定义任务,确保构建过程符合项目需求。

在gradle执行的生命周期内插入代码

在初始化阶段插入代码

在 Gradle 的 初始化阶段 (Initialization Phase),可以通过 gradle.beforeProjectgradle.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.afterProjectgradle.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 {} 适用于:

  1. 任务创建后任务执行前 进行修改。
  2. 插件完成初始化 后,修改插件的行为。
  3. build.gradle 配置完成后 动态添加任务修改已存在的任务
  4. 确保 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.beforeProjectgradle.projectsLoaded 来插入代码。
  • 配置阶段 可以用 gradle.afterProjectgradle.projectsEvaluated 来插入代码。
  • 执行阶段 可以用 tasks.whenTaskAddeddoFirstdoLast 来插入代码。
  • 构建完成后 可以用 gradle.buildFinished 处理成功或失败的情况。
相关推荐
星之卡比*2 分钟前
前端面试题---vite和webpack的区别
前端·面试
^^为欢几何^^6 分钟前
npm、pnpm和yarn有什么区别
前端·npm·node.js
AC-PEACE28 分钟前
Vue 中 MVVM、MVC 和 MVP 模式的区别
前端·vue.js·mvc
播播资源31 分钟前
ChatGPT付费创作系统V3.1.3独立版 WEB端+H5端+小程序端 (DeepSeek高级通道+推理输出格式)安装教程
前端·ai·chatgpt·ai作画·小程序·deepseek·deepseek-v3
zhrb1 小时前
打开Firefox自动打开hao360.hjttif.com标签解决方案
前端·firefox
安大桃子1 小时前
Cesium实现深色地图效果
前端·gis·cesium
程楠楠&M1 小时前
uni-app(位置1)
前端·javascript·uni-app·node.js
破z晓1 小时前
uniapp 整合openlayers 编辑图形文件并上传到服务器
前端·javascript·uni-app
码农君莫笑1 小时前
Linux系统上同时打印到物理打印机并生成PDF副本方法研究
linux·前端·chrome·打印·信管通
xlxxy_2 小时前
ABAP数据库表的增改查
开发语言·前端·数据库·sql·oracle·excel