gradle中dependsOn、doFirst和doLast的关系

264 阅读2分钟

在Gradle构建系统中,dependsOndoFirstdoLast是三个核心的任务配置方法,它们各自有不同的职责但又相互配合,共同构成了灵活的任务执行机制。

一、三者的本质区别

方法作用域执行时机主要用途
dependsOn任务间关系当前任务执行前先执行依赖任务定义任务依赖关系
doFirst任务内部任务自身执行时最先执行添加任务前置操作
doLast任务内部任务自身执行时最后执行添加任务后置操作

二、执行顺序的完整生命周期

deepseek_mermaid_20250627_bda84f.png

三、在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

四、三者的协同工作原理

  1. 层级关系

    • dependsOn控制任务间的执行顺序
    • doFirst/doLast控制任务内部的执行顺序
  2. 执行流程

    • Gradle先解析dependsOn关系,构建任务图

    • 执行时,对每个任务按顺序执行:

      1. 所有doFirst闭包(按添加顺序)
      2. 任务原有逻辑
      3. 所有doLast闭包(按添加顺序)
  3. 添加顺序不影响执行顺序

    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"
    }
}

七、总结:三者的协作关系

  1. dependsOn

    • 定义任务间的必须执行顺序
    • 建立任务依赖关系图
    • 保证前提条件满足
  2. doFirst

    • 添加任务开始执行时的操作
    • 适合准备工作和前置检查
    • 可添加多个,按添加逆序执行
  3. doLast

    • 添加任务完成前的操作
    • 适合清理工作和结果处理
    • 可添加多个,按添加顺序执行