前言
上一篇文章Kts Gradle学习 主要是学习新版本的Kotlin Gradle相关配置和以前的Groovy的不同表现方式,这里需要为后边自定义插件编写做准备,所以还是要学习下Gradle的生命周期和一些命令或者Task的基础知识 (ps:最早是20年看的一个大佬的文章入门,时隔多年 重新整理和回顾,在AI的大浪潮下 沉下心稳扎稳打基本功也是可以的)
Gradle生命周期
Gradle 构建分为三个阶段:
初始化阶段 (Initialization)
- 读取
settings.gradle.kts文件,生成Settings对象,确定哪些项目需要参与构建,为需要构建的项目创建Project对象;
配置阶段 (Configuration)
- 配置阶段主要是解析
build.gradle.kts文件,配置init阶段生成的Project对象,构建根项目和所有子项目,同时生成和配置在build.gradle.kts中定义的Task对象,构造Task的关系依赖图,关系依赖图是一个有向无环图;
执行阶段 (Execution)
- 根据configure阶段的关系依赖图执行Task.
生命周期钩子实例
可以借助Gradle 留给我们的钩子 ,在 build.gradle.kts 和子中,来打印一下生命周期,熟悉下钩子的使用
kotlin
// 配置阶段钩子
beforeEvaluate {
println("3. 项目配置开始")
}
afterEvaluate {
println("4. 项目配置完成")
}
gradle.projectsEvaluated {
println("5. 所有项目配置完成")
}
// 执行阶段钩子
gradle.taskGraph.whenReady {
println("6. Task 图准备完成")
}
gradle.taskGraph.beforeTask {
println("7. Task 执行前:$path")
}
gradle.taskGraph.afterTask {
println("8. Task 执行后:$path")
}
gradle.buildFinished {
println("9. 构建完成")
}
工程中钩子使用
根目录下的 build.gradle groovy 格式
groovy
// 钩子3:每个项目配置开始前
gradle.beforeProject { project ->
println "\n [配置阶段] ${project.name} 开始配置"
}
// 钩子4:每个项目配置完成后
gradle.afterProject { project ->
println " [配置阶段] ${project.name} 配置完成"
// 统计信息
def taskCount = project.tasks.size()
def depCount = project.configurations.size()
println " Tasks: ${taskCount} | Configurations: ${depCount}"
}
// 钩子5:所有项目配置完成
gradle.projectsEvaluated {
println "\n [配置阶段] 所有项目配置完成"
// 统计所有项目的 Task 数量
def totalTasks = gradle.rootProject.allprojects.sum { it.tasks.size() }
println " 总 Task 数量:${totalTasks}"
}
// 钩子6:Task 图准备完成(知道要执行哪些 Task)
gradle.taskGraph.whenReady { taskGraph ->
println "\n [执行阶段] Task 执行计划"
def tasks = taskGraph.allTasks
println "将执行 ${tasks.size()} 个 Task:\n"
// 按项目分组显示
tasks.groupBy { it.project.name }.each { projectName, projectTasks ->
println " ${projectName}:"
projectTasks.each { task ->
println " ✓ ${task.name}"
}
println ""
}
}
// 钩子7:每个 Task 执行前
gradle.taskGraph.beforeTask { task ->
println " [执行] 开始:${task.path}"
}
// 钩子8:每个 Task 执行后
gradle.taskGraph.afterTask { task ->
def status = task.state.failure != null ? " 失败" : " 成功"
def skipped = task.state.skipped ? " (跳过)" : ""
println "${status} [执行] 完成:${task.path}${skipped}"
}
// 钩子9:构建完成(成功或失败)
gradle.buildFinished { result ->
if (result.failure != null) {
println "\n Gradle 构建失败"
println "\n错误:${result.failure?.message}"
} else {
println "\n Gradle 构建成功"
}
}
Library 模块build.gradle.kts kts格式
kotlin
val projectStartTime = System.currentTimeMillis()
println("\n [${project.name}] 配置阶段开始")
// 钩子:当前项目配置完成
afterEvaluate {
val configTime = System.currentTimeMillis() - projectStartTime
println(" [${project.name}] 配置完成,耗时:${configTime}ms")
// 显示 Android 配置信息
android.apply {
println("Android 配置:")
println("- compileSdk: $compileSdk")
println("- minSdk: ${defaultConfig.minSdk}")
println("- targetSdk: ${defaultConfig.targetSdk}")
println("- versionCode: ${defaultConfig.versionCode}")
println("- versionName: ${defaultConfig.versionName}")
}
}
// Task 级别监控(带性能分析)
tasks.configureEach {
doFirst {
// 记录每个 Task 的开始时间
ext.set("taskStartTime", System.currentTimeMillis())
println("[$path] 任务开始执行...")
}
doLast {
// 计算每个 Task 的耗时
val start = ext.get("taskStartTime") as Long
val duration = System.currentTimeMillis() - start
// 根据耗时分类
val mm = when {
duration > 5000 -> "垃圾速度"
duration > 2000 -> "正常速度"
duration > 1000 -> "飞速速度"
duration > 0 -> "闪电速度"
else -> " ??? "
}
println("$mm [$path] 任务完成,耗时:${duration}ms")
if (duration > 5000) {
println("警告:任务耗时过长,建议优化!")
}
}
}
// 特定 Task 的钩子
// 监控编译 Task
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
doFirst {
println("Kotlin 编译开始...")
}
doLast {
println("Kotlin 编译完成")
}
}
// 监控打包 Task(Library 模块使用 bundleDebugAar)
tasks.matching { it.name == "bundleDebugAar" }.configureEach {
doFirst {
println("\n 开始打包 Debug AAR...")
}
doLast {
println(" Debug AAR 打包完成!")
// 显示 AAR 信息
val aarDir = File(layout.buildDirectory.asFile.get(), "outputs/aar")
if (aarDir.exists()) {
aarDir.listFiles()?.forEach { aar ->
if (aar.extension == "aar") {
val size = aar.length() / 1024.0 / 1024.0
println("AAR: ${aar.name}")
println("大小: ${"%.2f".format(size)} MB")
}
}
}
}
}
tasks.matching { it.name == "bundleReleaseAar" }.configureEach {
doFirst {
println("\n 开始打包 Release AAR...")
}
doLast {
println(" Release AAR 打包完成!")
// 显示 AAR 信息
val aarDir = File(layout.buildDirectory.asFile.get(), "outputs/aar")
if (aarDir.exists()) {
aarDir.listFiles()?.forEach { aar ->
if (aar.extension == "aar") {
val size = aar.length() / 1024.0 / 1024.0
println("AAR: ${aar.name}")
println("大小: ${"%.2f".format(size)} MB")
}
}
}
}
}
查看gradle 运行结果可见
yaml
Starting Gradle Daemon...
Gradle Daemon started in 854 ms
> Configure project :
[配置阶段] GradleY 配置完成
Tasks: 17 | Configurations: 0
> Configure project :app
[配置阶段] app 开始配置
[配置阶段] app 配置完成
Tasks: 35 | Configurations: 140
> Configure project :GroovyLibrary
[配置阶段] GroovyLibrary 开始配置
[配置阶段] GroovyLibrary 配置完成
Tasks: 33 | Configurations: 80
> Configure project :KtsLibrary
[配置阶段] KtsLibrary 开始配置
[KtsLibrary] 配置阶段开始
[配置阶段] KtsLibrary 配置完成
Tasks: 34 | Configurations: 80
[KtsLibrary] 配置完成,耗时:21ms
Android 配置:
- compileSdk: 35
- minSdk: 24
- targetSdk: null
- versionCode: null
- versionName: null
[配置阶段] 所有项目配置完成
总 Task 数量:687
[执行阶段] Task 执行计划
将执行 1 个 Task:
GradleY:
✓ prepareKotlinBuildScriptModel
> Task :prepareKotlinBuildScriptModel UP-TO-DATE
[执行] 开始::prepareKotlinBuildScriptModel
成功 [执行] 完成::prepareKotlinBuildScriptModel (跳过)
Download https://maven.aliyun.com/repository/public/javax/inject/javax.inject/1/javax.inject-1-javadoc.jar, took 36 ms
Gradle 构建成功
BUILD SUCCESSFUL in 24s
如果想查看aar 执行情况可以输出 ./gradlew :KtsLibrary:assemble --no-daemon (gradlew即Gradle Wrapper的简写)
常用命令
下列主要是自己平时使用到的命令,有时候会忘记,这里也捎带上
bash
# 查看 Gradle 版本
./gradlew --version
# 查看所有可用 Task
./gradlew tasks
# 查看所有 Task(包括隐藏的)
./gradlew tasks --all
# 查看项目结构
./gradlew projects
# 清理构建产物
./gradlew clean
# 清理并构建
./gradlew clean build
======== Android 构建 ==========
# 构建所有变体
./gradlew assemble
# 构建 Debug 版本
./gradlew assembleDebug
# 构建失败时,查看详细错误
./gradlew assembleDebug --info --stacktrace
# 构建 Release 版本
./gradlew assembleRelease
# 安装 Debug 版本
./gradlew installDebug
# 安装 Release 版本
./gradlew installRelease
# 卸载 Debug 版本
./gradlew uninstallDebug
# 卸载 Release 版本
./gradlew uninstallRelease
=========== 查看依赖 ==========
# 查看所有依赖 并输出到文件中
./gradlew app:dependencies > dependencies.txt
# 查看特定配置的依赖
./gradlew :app:dependencies --configuration implementation
./gradlew :app:dependencies --configuration debugRuntimeClasspath
# 查看依赖树,找 (*) 标记
./gradlew :app:dependencies --configuration debugRuntimeClasspath | grep "(*)"
# 查看具体的依赖路径
./gradlew :app:dependencyInsight --dependency okhttp --configuration debugRuntimeClasspath
# 查看任务
./gradlew tasks
还有一些可以自己拼接在后边的 重要参数
gradle
# 调试
--info # 详细信息
--stacktrace # 栈追踪
--scan # 构建扫描
# 性能
--parallel # 并行
--build-cache # 构建缓存
--configuration-cache # 配置缓存
这里只是简单塞了一些 常用的命令
Task 任务
Task 基本概念
Task(任务)是Gradle中最小的构建单元,但是 Task中又会有很多的Action 是Task中最小的执行单元。 Gradle 构建的核心是由Task组成的有向无环图,所以 各个Task之间是有运行顺序的,不会出现循环依赖等问题。 Task属于 Project对象,所以可以放在根目录或者子目录的 build.gradle.kts文件中。

Task 使用
创建 (推荐使用register 懒加载)
kotlin
register(String name, Action<? super Task> configurationAction)
register(String name, Class type, Action<? super T> configurationAction)
// 最简单的Task 不推荐使用 create 直接创建
tasks.register("hello") {
doLast {
println(it.name + " Gradle!")
}
}
// 带类型的Task 其他的还有 Delete、Zip、Jar等类型
tasks.register<Copy>("copyFiles") {
from("src/main/resources")
into("build/resources")
}
// 带配置的Task
tasks.register("printInfo") {
group = "custom"
description = "打印项目信息"
doLast {
println("Project: ${project.name}")
println("Version: ${project.version}")
}
}
// 使用案例
./gradlew hello
// 也可以多个Task 执行 (但是Task 的命名是不能重复的)
./gradlew task1 task2 task3
doFirst 和 doLast
kotlin
tasks.register("greeting") {
// doLast:在最后执行
doLast {
println("3. Goodbye!")
}
// doFirst:在最前面执行
doFirst {
println("2. Hello!")
}
// 再添加一个doLast 最后添加的 doLast 最后执行
doLast {
println("4. See you!")
}
// 再添加一个doFirst 最后添加的 doFirst第一个执行
doFirst {
println("1. Welcome!")
}
}
运行结果
markdown
> Task :greeting
1. Welcome!
2. Hello!
3. Goodbye!
4. See you!
自定义Task
kotlin
abstract class GreetingTask : DefaultTask() {
@Input
var greeting: String = "Hello"
@Input
var name: String = "World"
@TaskAction
fun greet() {
println("$greeting, $name!")
}
}
// 使用自定义Task
tasks.register<GreetingTask>("greet") {
greeting = "你好"
name = "Gradle"
}
Task 常用属性
kotlin
tasks.register("myTask") {
// 任务名称(只读)
println("Task name: $name")
// 任务分组
group = "custom"
// 任务描述
description = "这是一个自定义任务"
// 是否启用
enabled = true
// 超时时间
timeout.set(Duration.ofMinutes(5))
// 依赖的Task
dependsOn("clean")
doLast {
println("Executing $name")
}
}
Task依赖关系
- dependsOn(强依赖)
kotlin
tasks.register("taskA") {
doLast {
println("执行 TaskA")
}
}
tasks.register("taskB") {
doLast {
println("执行 TaskB")
}
}
tasks.register("taskC") {
// taskC依赖taskA和taskB
dependsOn("taskA", "taskB")
doLast {
println("执行 TaskC")
}
}
执行 ./gradlew taskC: 是必须先执行依赖项的, 有向无环的
arduino
> Task :taskA
执行 TaskA
> Task :taskB
执行 TaskB
> Task :taskC
执行 TaskC
- mustRunAfter (在谁之后 必须的,会报错的那种)
kotlin
tasks.register("compile") {
doLast {
println("编译代码")
}
}
tasks.register("test") {
// 如果test要执行,必须在compile之后
mustRunAfter("compile")
doLast {
println("运行测试")
}
}
执行 ./gradlew compile test:
shell
> Task :compile
编译代码
> Task :test
运行测试
执行 ./gradlew test compile(顺序相反):
shell
> Task :compile ← 依然先执行compile
编译代码
> Task :test
运行测试
只执行 ./gradlew test:
shell
> Task :test ← 只执行test,不会自动执行compile
运行测试
- shouldRunAfter (在谁之后 不强制,不会报错)
kotlin
tasks.register("taskA") {
doLast {
println("TaskA")
}
}
tasks.register("taskB") {
// 建议在taskA之后执行
shouldRunAfter("taskA")
doLast {
println("TaskB")
}
}
- finalizedBy (结束后执行 某Task)
kotlin
tasks.register("buildApp") {
// 构建完成后,必须执行清理
finalizedBy("cleanup")
doLast {
println("构建应用")
}
}
tasks.register("cleanup") {
doLast {
println("清理临时文件")
}
}
执行 ./gradlew buildApp:
arduino
> Task :buildApp
构建应用
> Task :cleanup
清理临时文件
Task跳过
- onlyIf (条件跳过)
kotlin
tasks.register("deployProd") {
// 只有在生产环境才执行
onlyIf {
project.hasProperty("prod")
}
doLast {
println("部署到生产环境")
}
}
执行:
bash
// 不会执行
./gradlew deployProd
// 会执行
./gradlew deployProd -Pprod
- StopExecutionException (异常跳过)
- enabled
kotlin
tasks.register("oldTask") {
// 禁用这个Task
enabled = false
doLast {
println("这个Task已被禁用")
}
}
- timeout (超时跳过)
kotlin
tasks.register("longRunningTask") {
timeout.set(Duration.ofSeconds(10))
doLast {
println("开始执行长时间任务")
Thread.sleep(15000)
println("任务完成")
}
}
执行结果:
arduino
Execution failed for task ':longRunningTask'.
> Timeout has been exceeded
Task执行状态
运行项目 有些Task后边会带一些大写后缀,但是有些Task可能后边没有,一般是指运行说明
ruby
> Task :LiveBundle:LiveAudience:mergeReleaseNativeLibs NO-SOURCE
> Task :LiveBundle:LiveAudience:stripReleaseDebugSymbols NO-SOURCE
> Task :LiveBundle:LiveAudience:copyReleaseJniLibsProjectOnly UP-TO-DATE
> Task :TingMainHost:Application:mergeAnd-f5-ocpaReleaseAssets FROM-CACHE
> Task :LiveBundle:Anchor:compileReleaseKotlin UP-TO-DATE
> Task :LiveBundle:LiveHome:compileReleaseKotlin UP-TO-DATE
| 标签 | 说明 | 示例场景 |
|---|---|---|
| (无标签) | Task正常执行 | 编译、打包等 |
UP-TO-DATE |
输入输出未改变,跳过执行 | 增量构建 |
FROM-CACHE |
从构建缓存中复用结果 | 开启Build Cache |
SKIPPED |
Task被跳过 | 条件不满足、被排除 |
NO-SOURCE |
没有源文件需要处理 | 空的源码目录 |
Task增量构建
增量构建 是当Task的输入和输出没有变化时,跳过action的执行,当Task输入或输出发生变化时,在action中只对发生变化的输入或输出进行处理,这样就可以避免一个没有变化的Task被反复构建,当Task发生变化时也只处理变化部分,这样可以提高Gradle的构建效率,缩短构建时间。
Task增量构建判断流程:
- 检查输入是否改变(文件内容、属性值等)
- 检查输出是否存在
- 如果输入未变且输出存在 → UP-TO-DATE
- 否则 → 重新执行
模拟示例:
kotlin
abstract class IncrementalTask : DefaultTask() {
@InputFile
abstract val inputFile: RegularFileProperty
@OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun execute() {
val input = inputFile.get().asFile
val output = outputFile.get().asFile
println("处理文件:${input.name}")
// 读取输入,处理后写入输出
val content = input.readText()
output.writeText(content.uppercase())
}
}
tasks.register<IncrementalTask>("processFile") {
inputFile.set(file("input.txt"))
outputFile.set(file("build/output.txt"))
}
首次执行:
bash
$ ./gradlew processFile
> Task :processFile
处理文件:input.txt
再次执行(input.txt未改变):
bash
$ ./gradlew processFile
// 发现文件没有改变 跳过执行
> Task :processFile UP-TO-DATE
修改input.txt后再次执行:
bash
$ ./gradlew processFile
// 发现文件产生了改变 重新执行
> Task :processFile
处理文件:input.txt
关于输入和输出,上边的自定义Task注解代表的含义简单列举下常用的: 常用的注解
| 注解 | 作用 | 示例 |
|---|---|---|
@Input |
标记输入属性 | 字符串、数字等 |
@InputFile |
标记输入文件 | 单个文件 |
@InputFiles |
标记输入文件集合 | 多个文件 |
@InputDirectory |
标记输入目录 | 目录 |
@OutputFile |
标记输出文件 | 单个文件 |
@OutputFiles |
标记输出文件集合 | 多个文件 |
@OutputDirectory |
标记输出目录 | 目录 |
@Internal |
标记内部属性(不参与增量构建) | 临时变量 |
禁用增量构建
kotlin
tasks.register("alwaysRun") {
// 禁用UP-TO-DATE检查
outputs.upToDateWhen { false }
doLast {
println("每次都执行")
}
}
最后
主要学习了 Task 基础知识吧,都是为了最后的自定义插件工作的,基础但是是必要的。