在Gradle构建系统中,dependsOn、doFirst和doLast是三个核心的任务配置方法,它们各自有不同的职责但又相互配合,共同构成了灵活的任务执行机制。
一、三者的本质区别
| 方法 | 作用域 | 执行时机 | 主要用途 |
|---|---|---|---|
| dependsOn | 任务间关系 | 在当前任务执行前先执行依赖任务 | 定义任务依赖关系 |
| doFirst | 任务内部 | 在任务自身执行时最先执行 | 添加任务前置操作 |
| doLast | 任务内部 | 在任务自身执行时最后执行 | 添加任务后置操作 |
二、执行顺序的完整生命周期
三、在Android项目中的典型配合使用
1. 完整构建任务示例
// 1. 定义准备任务
task setupEnvironment {
doLast {
println "初始化构建环境..."
}
}
// 2. 定义检查任务
task runChecks {
dependsOn setupEnvironment // 依赖环境准备
doFirst {
println "开始代码检查..."
}
doLast {
println "代码检查完成"
}
}
// 3. 主构建任务
task build {
dependsOn runChecks // 依赖检查任务
doFirst {
println "开始构建..."
project.ext.startTime = System.currentTimeMillis()
}
doLast {
def duration = System.currentTimeMillis() - project.ext.startTime
println "构建完成,耗时${duration}ms"
}
}
执行gradle build输出:
> Task :setupEnvironment
初始化构建环境...
> Task :runChecks
开始代码检查...
代码检查完成
> Task :build
开始构建...
构建完成,耗时XXms
四、三者的协同工作原理
-
层级关系:
dependsOn控制任务间的执行顺序doFirst/doLast控制任务内部的执行顺序
-
执行流程:
-
Gradle先解析
dependsOn关系,构建任务图 -
执行时,对每个任务按顺序执行:
- 所有
doFirst闭包(按添加顺序) - 任务原有逻辑
- 所有
doLast闭包(按添加顺序)
- 所有
-
-
添加顺序不影响执行顺序:
task example { // 后添加的doFirst仍最先执行 doLast { println "最后添加的doLast" } doFirst { println "最后添加的doFirst" } doLast { println "首先添加的doLast" } doFirst { println "首先添加的doFirst" } }最后添加的doFirst 首先添加的doFirst [任务原有逻辑] 首先添加的doLast 最后添加的doLast
五、最佳实践与常见错误
1. 推荐使用模式
task qualityCheck {
// 1. 声明依赖
dependsOn 'staticAnalysis', 'unitTests'
// 2. 添加前置操作
doFirst {
println "开始质量检查..."
startTimer()
}
// 3. 添加后置操作
doLast {
generateReport()
println "质量检查完成,耗时${getDuration()}ms"
}
}
2. 常见错误及修正
错误1:混淆dependsOn与执行顺序
// ❌ 错误理解
taskA.doFirst { taskB.execute() } // 手动执行是反模式
// ✅ 正确做法
taskA.dependsOn taskB
错误2:在配置阶段执行操作
// ❌ 错误:在配置阶段执行逻辑
task badExample {
dependsOn someTask
println "这会在配置阶段执行!"
doLast {
println "这才在执行阶段执行"
}
}
// ✅ 正确:所有逻辑放在doFirst/doLast中
task goodExample {
dependsOn someTask
doFirst {
println "配置阶段不会执行"
}
}
错误3:过度依赖doFirst
// ❌ 不推荐:用doFirst替代dependsOn
task processData {
doFirst {
// 准备数据
prepareData()
}
}
// ✅ 推荐:拆分为独立任务
task prepareData {
// 准备数据逻辑
}
task processData {
dependsOn prepareData
// 处理数据逻辑
}
六、在Android复杂构建中的应用
1. 多模块依赖管理
// 在app模块的build.gradle中
task assembleFull {
dependsOn ':data:assemble', ':domain:assemble', 'assemble'
doFirst {
println "开始全模块构建..."
}
doLast {
println "所有模块构建完成"
generateCombinedReport()
}
}
2. 条件性任务依赖
task build {
// 根据属性决定是否包含测试
if (project.hasProperty('includeTests')) {
dependsOn 'test'
}
dependsOn 'assemble'
doLast {
if (project.hasProperty('upload')) {
uploadArtifacts()
}
}
}
3. 构建时间分析
android.applicationVariants.all { variant ->
variant.assembleProvider.get().doFirst {
project.ext."${variant.name}StartTime" = System.currentTimeMillis()
}
variant.assembleProvider.get().doLast {
def duration = System.currentTimeMillis() - project.ext."${variant.name}StartTime"
println "${variant.name}构建耗时: ${duration}ms"
}
}
七、总结:三者的协作关系
-
dependsOn:
- 定义任务间的必须执行顺序
- 建立任务依赖关系图
- 保证前提条件满足
-
doFirst:
- 添加任务开始执行时的操作
- 适合准备工作和前置检查
- 可添加多个,按添加逆序执行
-
doLast:
- 添加任务完成前的操作
- 适合清理工作和结果处理
- 可添加多个,按添加顺序执行