通俗的理解 Gradle 构建流程

218 阅读3分钟

这篇文章主要带你探索 Gradle 的核心技术,用通俗的例子讲透 Gradle 构建流程、Project 和 Task 的使用技巧,以及如何自定义任务插入到 Android 构建流程中。以下是核心内容总结:

一、Gradle 构建的三阶段 “流水线”

  1. 初始化阶段
    就像开工前的准备会议,Gradle 会先执行settings.gradle,确定要构建哪些模块(比如 app 模块),并为每个模块创建对应的 Project 对象。

    • 例子:include ':app'就是告诉 Gradle “我要构建 app 模块”。
  2. 配置阶段
    相当于制定详细的施工计划,Gradle 会解析每个模块的build.gradle,构建任务依赖图(比如编译代码、打包 APK 的任务顺序)。

    • 注意:这里会执行除了 Task 具体动作外的所有代码,比如变量定义、插件引用。
  3. 执行阶段
    真正开始干活,按顺序执行任务(Task)。比如先编译 Java 代码,再打包资源,最后生成 APK。

    • 例子:执行./gradlew build时,Gradle 会按依赖图依次运行compileDebugJavaWithJavacassembleDebug等任务。

二、Gradle 生命周期:每个阶段的 “闹钟提醒”

Gradle 在每个阶段都提供了回调钩子,就像闹钟一样提醒你在特定时刻做事情:

  • 初始化完成后:gradle.projectsLoaded

  • 配置每个模块前:gradle.beforeProject

  • 配置每个模块后:gradle.afterProject

  • 任务执行前:gradle.taskGraph.beforeTask

  • 所有任务完成后:gradle.buildFinished

应用场景:用这些钩子打印构建耗时,比如计算从初始化到执行完成的总时间,定位性能瓶颈。

三、Project 对象:模块的 “指挥官”

每个build.gradle对应一个 Project 对象,它负责管理模块的所有配置:

  1. 常用功能

    • 获取项目路径:getRootDir()(根目录)、getBuildDir()(构建输出目录)
    • 引用插件:apply plugin: 'com.android.application'
    • 管理依赖:dependencies { implementation 'xxx:xxx' }
  2. 扩展属性:全局共享的 “便利贴”
    build.gradlegradle.properties中定义全局变量,比如:

    groovy

    ext {
        androidConfig = [compileSdkVersion: 30, targetSdkVersion: 30]
    }
    

    好处是统一管理版本号、依赖库,避免重复代码。

四、Task:Gradle 的 “最小工作单元”

Task 是 Gradle 执行的最小任务,比如 “编译代码”“拷贝文件”“清理构建产物”:

  1. Task 的 “动作时机”

    • doFirst:任务开始时执行
    • doLast:任务结束时执行

    groovy

    task cleanApk {
        doLast {
            delete "app/build/outputs/apk" // 执行时删除APK文件
        }
    }
    
  2. Task 依赖管理:任务的 “先后顺序”

    • dependsOn:强依赖,比如taskB dependsOn taskA表示先执行 taskA 再执行 taskB
    • mustRunAfter:指定执行顺序,但不强制依赖
    • finalizedBy:任务完成后执行另一个任务,比如taskA finalizedBy taskB表示 taskA 完成后执行 taskB
  3. Task 类型:现成的 “工具包”
    Gradle 自带多种 Task 类型,比如:

    • Copy:拷贝文件
    • Delete:删除文件
    • JavaCompile:编译 Java 代码

五、自定义 Task 插入 Android 构建流程

想在 Android 构建过程中插入自己的任务?有三种方法:

  1. 依赖绑定:让系统任务依赖你的任务

    groovy

    task myTask { doLast { println "我在打包前执行" } }
    tasks.findByName("mergeDebugResources").dependsOn(myTask) // 合并资源前执行myTask
    
  2. 后置执行:系统任务完成后执行你的任务

    groovy

    tasks.findByName("assembleDebug").finalizedBy(myTask) // 打包完成后执行myTask
    
  3. 顺序插入:在两个系统任务之间插入你的任务

    groovy

    myTask.mustRunAfter(tasks.findByName("mergeDebugResources"))
    tasks.findByName("processDebugResources").dependsOn(myTask)
    

六、实用命令:Gradle 的 “快捷操作”

  • ./gradlew tasks:查看所有任务
  • ./gradlew app:dependencies:查看模块依赖关系
  • ./gradlew build taskTree --no-repeat:查看任务依赖图

总结

这篇文章带你从 Gradle 的构建流程入手,学会了如何用 Project 管理模块配置、用 Task 定义具体任务,以及如何通过生命周期钩子和依赖管理将自定义任务插入到 Android 构建流程中。这些知识是自定义 Gradle 插件、优化构建流程的基础,后续还可以进一步探索 Transform、代码混淆等高级功能。