Gradle 现代化任务依赖方案
传统任务依赖模式的痛点
🧩 典型传统实现
kotlin
// 文件写入任务
open class OldWriter : DefaultTask() {
@get:Input
val list: MutableList<String> = mutableListOf()
@get:OutputFile
lateinit var outputFile: File
@TaskAction
fun write() = outputFile.writeText(list.joinToString("\n"))
}
// 文件读取任务
open class OldReader : DefaultTask() {
@get:InputFile
lateinit var inputFile: File
@get:OutputFile
lateinit var outputFile: File
@TaskAction
fun writeCount() = outputFile.writeText("Line count: ${inputFile.readLines().count()}")
}
// 任务配置
val oldWriterFile = project.buildDir.resolve("oldWriter.txt")
val oldWriterTask = tasks.register("oldWriter", OldWriter::class) {
list.addAll(listOf("apple", "orange", "banana"))
outputFile = oldWriterFile
}
tasks.register("oldReader", OldReader::class) {
inputFile = oldWriterFile
dependsOn(oldWriterTask) // 显式声明依赖
outputFile = project.buildDir.resolve("oldReader.txt")
}
⚠️ 架构缺陷分析
- 强耦合依赖声明
dependsOn(oldWriterTask)
成为关键负载点,重构时易被误删,导致任务链断裂
- 空安全风险
使用lateinit
伪非空声明,实际依赖手动初始化顺序,配置错误将引发运行时异常
- 静态文件路径
硬编码文件路径使任务难以复用,路径变更需多处修改
现代化Property API解决方案
🚀 改进后实现方案
kotlin
// 抽象化写入任务
abstract class NewWriter : DefaultTask() {
@get:Input
abstract val list: ListProperty<String> // 抽象属性
@get:OutputFile
abstract val outputFile: RegularFileProperty // 文件属性封装
@TaskAction
fun write() {
outputFile.get().asFile.writeText(list.get().joinToString("\n"))
}
}
// 抽象化读取任务
abstract class NewReader : DefaultTask() {
@get:InputFile
abstract val inputFile: RegularFileProperty
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun writeCount() {
val count = inputFile.get().asFile.readLines().count()
outputFile.get().asFile.writeText("Line count $count")
}
}
// 自动化连接配置
val newWriterTask = tasks.register("newWriter", NewWriter::class) {
list.addAll("apple", "orange", "banana")
outputFile.set(layout.buildDirectory.file("newWriter.txt")) // 延迟绑定路径
}
tasks.register("newReader", NewReader::class) {
// 自动建立任务依赖
inputFile.set(newWriterTask.flatMap { it.outputFile })
outputFile.set(layout.buildDirectory.file("newReader.txt"))
}
💡 核心技术突破
- 隐式依赖连接
flatMap
操作符自动建立任务依赖链:
kotlin
inputFile.set(newWriterTask.flatMap { it.outputFile })
-
当解析
inputFile
时自动触发newWriterTask
执行 -
依赖关系声明与任务逻辑解耦
- 类型安全属性
采用Gradle提供的属性抽象类:
kotlin
abstract val outputFile: RegularFileProperty
-
内置空安全检查
-
支持延迟计算(Lazy Evaluation)
- 动态路径绑定
通过layout.buildDirectory
动态构建路径:
kotlin
outputFile.set(layout.buildDirectory.file("newWriter.txt"))
-
路径变更不影响任务逻辑
-
支持跨项目复用
深度实践案例
🌐 多阶段数据处理流水线
kotlin
// 数据生成
abstract class DataGenTask : DefaultTask() {
@get:OutputFile
abstract val dataset: RegularFileProperty
@TaskAction
fun generate() {
dataset.get().asFile.writeText((1..1000).joinToString(","))
}
}
// 数据分析
abstract class DataAnalyzer : DefaultTask() {
@get:InputFile
abstract val sourceData: RegularFileProperty
@get:OutputFile
abstract val report: RegularFileProperty
@TaskAction
fun analyze() {
val data = sourceData.get().asFile.readText().split(",")
val stats = """
Count: ${data.size}
Min: ${data.minOf { it.toInt() }}
Max: ${data.maxOf { it.toInt() }}
""".trimIndent()
report.get().asFile.writeText(stats)
}
}
// 自动构建流水线
val genTask = tasks.register("generateData", DataGenTask::class) {
dataset.set(layout.buildDirectory.file("dataset.csv"))
}
tasks.register("analyzeData", DataAnalyzer::class) {
sourceData.set(genTask.flatMap { it.dataset }) // 自动连接
report.set(layout.buildDirectory.file("report.txt"))
}
🔧 进阶技巧:跨任务数据传递
kotlin
// 传递结构化数据
abstract class JsonProcessor : DefaultTask() {
@get:OutputFile
abstract val jsonOutput: RegularFileProperty
@TaskAction
fun process() {
val data = mapOf("items" to listOf("A", "B", "C"))
jsonOutput.get().asFile.writeText(Json.encodeToString(data))
}
}
abstract class DataLoader : DefaultTask() {
@get:InputFile
abstract val jsonInput: RegularFileProperty
@TaskAction
fun load() {
val content = jsonInput.get().asFile.readText()
val data = Json.decodeFromString<Map<String, List<String>>>(content)
println("Loaded items: ${data["items"]?.joinToString()}")
}
}
// 自动化JSON管道
val processor = tasks.register("processJson", JsonProcessor::class) {
jsonOutput.set(layout.buildDirectory.file("data.json"))
}
tasks.register("loadData", DataLoader::class) {
jsonInput.set(processor.flatMap { it.jsonOutput })
}
新旧方案对比评估
| 维度 | 传统dependsOn方案 | Property API方案 |
|---------------------|----------------------------------|--------------------------------|
| 依赖声明 | 显式声明dependsOn
| 隐式通过flatMap
连接 |
| 空安全 | 依赖lateinit
,运行时可能崩溃 | 编译时类型检查 |
| 任务触发时机 | 需手动管理执行顺序 | 按需自动触发依赖任务 |
| 配置重构安全性 | 高敏感,易因配置遗漏失败 | 低敏感,核心逻辑与配置解耦 |
| 跨模块复用 | 路径硬编码,复用困难 | 动态路径绑定,支持灵活复用 |
💎 总结:构建现代化的Gradle任务链
::: tabs
@tab 核心优势
- 依赖自动化
通过flatMap
实现任务自动连接,消除显式dependsOn
声明
- 类型安全革命
RegularFileProperty
等抽象属性取代lateinit var
,编译期保障空安全
- 资源延迟绑定
layout.buildDirectory.file()
实现路径动态解析,支持环境适配
@tab 最佳实践
- 抽象化任务
kotlin
abstract class ModernTask : DefaultTask() {
@get:InputFile
abstract val input: RegularFileProperty
}
- 链式连接
kotlin
taskB.input.set(taskA.flatMap { it.output })
- 动态路径
kotlin
output.set(layout.buildDirectory.file("dynamic.txt"))
@tab 迁移路线
:::
当代码中出现
dependsOn
时,应视为技术债务信号。现代Gradle的Property API通过类型安全的声明式编程,将任务连接从"命令式胶水代码"进化为"声明式数据管道",从根本上提升构建系统的可靠性和可维护性。🎯
原文:xuanhu.info/projects/it...