Gradle 中的 Task 是什么?如何创建和配置 Task?
核心答案
Task 是 Gradle 构建的最小执行单元,代表一个原子操作(编译、打包、测试等)。每个 Task 包含名称、类型、动作(Action)、依赖关系和输入/输出。创建 Task 有三种方式,推荐使用 tasks.register() 实现延迟初始化,避免配置阶段不必要的开销。
创建 Task
register vs create vs task 关键字
groovy// 推荐:延迟创建,仅在实际需要时实例化 tasks.register('myTask') { doLast { println 'Executing myTask' } } // 立即创建,配置阶段就会实例化 tasks.create('myTask') { doLast { println 'Executing myTask' } } // DSL 快捷方式,等同 create task myTask { doLast { println 'Executing myTask' } }
register 与 create 的核心区别在于:register 返回 TaskProvider,Task 实例在首次被引用时才创建。大型项目中大量使用 create 会导致配置阶段耗时增加,register 可显著改善构建性能。
指定 Task 类型
groovytasks.register('copyFiles', Copy) { from 'src/main/resources' into 'build/resources' }
继承内置类型(Copy、Delete、Exec 等)可以复用已有的文件操作逻辑,不必从零实现。
配置 Task
groovytasks.register('myTask') { group = 'Custom Tasks' // Gradle 面板中的分组 description = '自定义任务描述' // gradle tasks 时的说明 dependsOn 'clean', 'compileJava' // 依赖其他任务 mustRunAfter 'test' // 硬排序约束 shouldRunAfter 'build' // 软排序约束(可被 Gradle 忽略) inputs.file('config.properties') // 声明输入 outputs.dir('build/output') // 声明输出 doFirst { println '执行前' } doLast { println '执行后' } }
声明 inputs/outputs 的意义在于启用增量构建:Gradle 检测到输入输出未变化时会跳过 Task 执行,这是提升构建速度的关键机制。
依赖与排序
- dependsOn:声明必须先完成的依赖,Gradle 据此构建有向无环图(DAG)决定执行顺序
- mustRunAfter:硬约束,两个任务同时被调度时保证先后顺序,但不引入依赖
- shouldRunAfter:软约束,Gradle 在并行执行优化时可以忽略
- finalizedBy:无论当前任务成功或失败,都会执行收尾任务(如资源清理)
groovytasks.register('taskA') { finalizedBy 'cleanup' doLast { println 'Task A' } } tasks.register('cleanup') { doLast { println 'Cleanup' } }
执行控制
groovy// 条件执行 tasks.register('conditionalTask') { onlyIf { project.hasProperty('runConditional') } doLast { println '条件满足才执行' } } // 禁用任务 tasks.register('disabledTask') { enabled = false } // 抛出 StopExecutionException 跳过当前动作 doFirst { if (!project.hasProperty('force')) { throw new StopExecutionException() } }
追问
register 创建的 Task 如何在其他地方引用? 使用 tasks.named('myTask') 获取 TaskProvider,再通过 .configure { } 追加配置。不要对 register 的 Task 直接用 tasks.myTask,那会触发立即实例化,失去延迟初始化的优势。
mustRunAfter 和 dependsOn 有什么区别? dependsOn 会建立真正的依赖关系,被依赖的任务一定会执行;mustRunAfter 只是排序约束,仅当两个任务都在本次构建的执行计划中时才生效,不会把对方拉入执行图。
为什么不要在 Task 体中直接写逻辑而要用 doFirst/doLast? Task 闭包中的代码在配置阶段执行,而 doFirst/doLast 中的代码在执行阶段执行。配置阶段写逻辑会导致每次运行任何 Task 都会执行这些代码,产生副作用和性能浪费。