xDocxDoc
AI
前端
后端
iOS
Android
Flutter
AI
前端
后端
iOS
Android
Flutter
  • Gradle 现代化任务依赖方案

Gradle 现代化任务依赖方案

传统任务依赖模式的痛点

🧩 典型传统实现

// 文件写入任务
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")
}

⚠️ 架构缺陷分析

  1. 强耦合依赖声明
    dependsOn(oldWriterTask)成为关键负载点,重构时易被误删,导致任务链断裂

  2. 空安全风险
    使用lateinit伪非空声明,实际依赖手动初始化顺序,配置错误将引发运行时异常

  3. 静态文件路径
    硬编码文件路径使任务难以复用,路径变更需多处修改


现代化Property API解决方案

🚀 改进后实现方案

// 抽象化写入任务
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"))
}

💡 核心技术突破

  1. 隐式依赖连接
    flatMap操作符自动建立任务依赖链:

    inputFile.set(newWriterTask.flatMap { it.outputFile })
    • 当解析inputFile时自动触发newWriterTask执行
    • 依赖关系声明与任务逻辑解耦
  2. 类型安全属性
    采用Gradle提供的属性抽象类:

    abstract val outputFile: RegularFileProperty
    • 内置空安全检查
    • 支持延迟计算(Lazy Evaluation)
  3. 动态路径绑定
    通过layout.buildDirectory动态构建路径:

    outputFile.set(layout.buildDirectory.file("newWriter.txt"))
    • 路径变更不影响任务逻辑
    • 支持跨项目复用

深度实践案例

🌐 多阶段数据处理流水线

// 数据生成
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"))
}

🔧 进阶技巧:跨任务数据传递

// 传递结构化数据
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任务链

核心优势
  1. 依赖自动化
    通过flatMap实现任务自动连接,消除显式dependsOn声明

  2. 类型安全革命
    RegularFileProperty等抽象属性取代lateinit var,编译期保障空安全

  3. 资源延迟绑定
    layout.buildDirectory.file()实现路径动态解析,支持环境适配

最佳实践
  1. 抽象化任务

    abstract class ModernTask : DefaultTask() {
        @get:InputFile
        abstract val input: RegularFileProperty
    }
  2. 链式连接

    taskB.input.set(taskA.flatMap { it.output })
  3. 动态路径

    output.set(layout.buildDirectory.file("dynamic.txt"))
迁移路线

当代码中出现dependsOn时,应视为技术债务信号。现代Gradle的Property API通过类型安全的声明式编程,将任务连接从“命令式胶水代码”进化为“声明式数据管道”,从根本上提升构建系统的可靠性和可维护性。🎯

最后更新: 2025/8/27 22:44