Gradle 相关

1,072 阅读5分钟

前言

  • 欢迎任何形式的转载,转载请保留原文链接:juejin.cn/post/699843…
  • 文章性质:非教程,意思是本文目的是便于使用时查阅,以及收集使用记录。所以看不明白的,请自行百度,这里不解释。当然欢迎指出存在的错误,以及新的用法
  • 文章主要内容:Gradle 相关笔记,包括配置、教程查找、语法记录,但不包括 Android 本身的 DSL 使用,Android DSL 使用见文章:《Android Gradle 常用配置》

相关资料:

  • 下载的 Gradle docs 路径,以 6.6 为例
    • 用户手册 PDF:gradle-6.6/docs/userguide/userguide.pdf
    • 用户手册:gradle-6.6/docs/userguide/userguide.html
    • DSL 手册:gradle-6.6/docs/dsl/index.html
    • JavaDoc 手册:gradle-6.6/docs/javadoc/index.html
    • 发行说明(即版本迭代说明):gradle-6.6/docs/release-notes.html
  • Gradle 官网 API 文档(等同上述 JavaDoc):docs.gradle.org/current/jav…
  • Groovy 官网文档:docs.groovy-lang.org/docs/latest…
  • Android Gradle 插件
    • developer.android.google.cn/reference/t…
    • 插件源码:Gradle 下载目录/caches/modules-2/files-2.1/com.android.tools.build/gradle/4.0.2/xxx/gradle-4.0.2-sources.jar
    • 其他相关源码:Gradle 下载目录/caches/modules-2/files-2.1/com.android.tools.build

Gradle 配置

本地仓库配置

配置环境变量 GRADLE_USER_HOME,并指向你的一个本地目录,这样 Gradle 下载的所有文件都会保存到这,在也不用担心 C 盘不够用了

Gralde 演示用例

这里为方便后续内容的讲解,这里贴出用例 gradle 代码,匹配的是 Android 项目

/setting.gradle

rootProject.name = 'AndroidCases'
include ':app-cases'
include ':lib-utils'
include ':lib-widget'

/build.gradle

buildscript {

    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.0.2'
    }
}

allprojects {
    repositories {
        mavenCentral()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

/lib-utils/build.gradle

Gradle 帮组任务列表

Gradle 本身提供了用于辅助的帮组性质的任务,执行这些任务,可以很方便的查看 gradle 的各种配置,如项目属性之类的

命令行:执行 gralde :lib-utils:tasks 后可以看到以下帮组任务列表

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':lib-utils'.
components - Displays the components produced by project ':lib-utils'. [incubating]
dependencies - Displays all dependencies declared in project ':lib-utils'.
dependencyInsight - Displays the insight into a specific dependency in project ':lib-utils'.
dependentComponents - Displays the dependent components of components in project ':lib-utils'. [incubating]
help - Displays a help message.
model - Displays the configuration model of project ':lib-utils'. [incubating]
outgoingVariants - Displays the outgoing variants of project ':lib-utils'.
projects - Displays the sub-projects of project ':lib-utils'.
properties - Displays the properties of project ':lib-utils'.
tasks - Displays the tasks runnable from project ':lib-utils'.

翻译说明(理解或翻译有误欢迎指出):

buildEnvironment - 显示模块 ':lib-utils' 声明的所有构建脚本依赖项
components - 显示模块 ':lib-utils' 生成的组件。[incubating]
dependencies - 显示模块 ':lib-utils' 声明的所有依赖项
dependencyInsight - 显示模块 ':lib-utils' 特定依赖项的洞察
dependentComponents - 显示模块 ':lib-utils' 组件的依赖组件。[incubating]
help - 显示帮助
model - 显示模块 ':lib-utils' 的配置模型。[incubating]
outgoingVariants - 显示模块 ':lib-utils' 的多样性输出信息(包括自定义,系统,多渠道)
projects - 显示模块 ':lib-utils' 子项目
properties - 显示模块 ':lib-utils' 属性
tasks - 显示模块 ':lib-utils' 所有可运行的任务

或者使用 IDEA 也能查看到

IDEA 查看方式

Gradle 生命周期

大方向上的生命周期

为方便说明与讲解,将上述用例加些日志,日志都以 println 开头,这代码可以不用看,直接看日志结果

/setting.gradle

println "settings.gradle begin"

rootProject.name = 'AndroidCases'
include ':app-cases'
include ':lib-utils'
include ':lib-widget'

// 构建监听
gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {
        println "监听:buildStarted"
    }

    @Override
    void settingsEvaluated(Settings settings) {
        println "监听:settingsEvaluated"
    }

    @Override
    void projectsLoaded(Gradle gradle) {
        println "监听:projectsLoaded"
    }

    @Override
    void projectsEvaluated(Gradle gradle) {
        println "监听:projectsEvaluated"
    }

    @Override
    void buildFinished(BuildResult result) {
        println "监听:buildFinished"
    }
})

// 各个项目监听
gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
    @Override
    void beforeEvaluate(Project project) {
        println "监听【${project}】beforeEvaluate"
    }

    @Override
    void afterEvaluate(Project project, ProjectState state) {
        println "监听【${project}】afterEvaluate"
    }
})

// 依赖配置监听
gradle.addListener(new DependencyResolutionListener() {
    @Override
    void beforeResolve(ResolvableDependencies dependencies) {
        println "监听【${dependencies}】beforeResolve"
    }

    @Override
    void afterResolve(ResolvableDependencies dependencies) {
        println "监听【${dependencies}】afterResolve"
    }
})

// 任务执行顺序图
gradle.addListener(new TaskExecutionGraphListener() {

    @Override
    void graphPopulated(TaskExecutionGraph graph) {
        println "监听:TaskExecutionGraphListener:${graph.getAllTasks()}"
    }
})

// 任务执行监听
gradle.addListener(new TaskExecutionListener() {
    @Override
    void beforeExecute(Task task) {
        println "监听【${task}】beforeExecute"
    }

    @Override
    void afterExecute(Task task, TaskState state) {
        println "监听【${task}】afterExecute"
    }
})

/**
 * 添加监听器,除了上述,还有这些,只是与生命周期无关
 * <li>{@link org.gradle.api.execution.TaskActionListener}
 * <li>{@link org.gradle.api.tasks.testing.TestListener}
 * <li>{@link org.gradle.api.tasks.testing.TestOutputListener}
 * <li>{@link org.gradle.api.logging.StandardOutputListener}
 */

println "settings.gradle end"

/build.gradle

println "/build.gradle 【${project}】 begin"

buildscript {
    println "/build.gradle buildscript 【${project}】 begin"

    repositories {
        google()
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.0.2'
    }

    println "/build.gradle buildscript 【${project}】 end"
}

allprojects {
    println "/build.gradle allprojects 【${project}】 begin"

    repositories {
        google()
        mavenCentral()
    }

    println "/build.gradle allprojects 【${project}】 end"
}

println "/build.gradle end"

/lib-utils/build.gradle

println "/lib-utils/build.gradle begin"

apply plugin: 'com.android.library'

// Android 本身的配置,此处省略

project.afterEvaluate {
    println "设置【${project}】afterEvaluate,但在这里设置 project.beforeEvaluate 是无效的"
}

println "/lib-utils/build.gradle end"

执行任意任务,以 gradle :lib-utils:bundleDebugAar 为例,日志结果为

Executing tasks: [bundleDebugAar] in project 省略\lib-utils

settings.gradle begin
settings.gradle end
监听:settingsEvaluated

监听:projectsLoaded

> Configure project :
监听【root project 'AndroidCases'】beforeEvaluate
/build.gradle buildscript 【root project 'AndroidCases'begin
/build.gradle buildscript 【root project 'AndroidCases'end
监听【dependencies ':classpath'】beforeResolve
监听【dependencies ':classpath'】afterResolve

/build.gradle 【root project 'AndroidCases'begin
/build.gradle allprojects 【root project 'AndroidCases'begin
/build.gradle allprojects 【root project 'AndroidCases'end
/build.gradle allprojects 【project ':app-cases'begin
/build.gradle allprojects 【project ':app-cases'end
/build.gradle allprojects 【project ':lib-utils'begin
/build.gradle allprojects 【project ':lib-utils'end
/build.gradle allprojects 【project ':lib-widget'begin
/build.gradle allprojects 【project ':lib-widget'end
/build.gradle end
监听【root project 'AndroidCases'】afterEvaluate

> Configure project :lib-utils
监听【project ':lib-utils'】beforeEvaluate
/lib-utils/build.gradle begin
/lib-utils/build.gradle end
监听【project ':lib-utils'】afterEvaluate
设置【project ':lib-utils'】afterEvaluate,但在这里设置 project.beforeEvaluate 是无效的

> Configure project :省略
监听:projectsEvaluated

监听【dependencies ':app-cases:classpath'】beforeResolve
监听【dependencies ':app-cases:classpath'】afterResolve
监听【dependencies ':lib-utils:classpath'】beforeResolve
监听【dependencies ':lib-utils:classpath'】afterResolve
监听【dependencies ':lib-widget:classpath'】beforeResolve
监听【dependencies ':lib-widget:classpath'】afterResolve
监听【dependencies ':lib-utils:debugCompileClasspath'】beforeResolve
监听【dependencies ':lib-utils:debugRuntimeClasspath'】beforeResolve
监听【dependencies ':lib-utils:debugRuntimeClasspath'】afterResolve
监听【dependencies ':lib-utils:debugCompileClasspath'】afterResolve
监听【dependencies ':lib-utils:lintClassPath'】beforeResolve
监听【dependencies ':lib-utils:lintClassPath'】afterResolve
监听【dependencies ':lib-utils:androidApis'】beforeResolve
监听【dependencies ':lib-utils:androidApis'】afterResolve
监听【dependencies ':lib-utils:debugAnnotationProcessorClasspath'】beforeResolve
监听【dependencies ':lib-utils:debugAnnotationProcessorClasspath'】afterResolve
监听【dependencies ':lib-utils:lintPublish'】beforeResolve
监听【dependencies ':lib-utils:lintPublish'】afterResolve

监听:TaskExecutionGraphListener:[task ':lib-utils:preBuild', task ':lib-utils:preDebugBuild', 省略, task ':lib-utils:bundleDebugAar']

> Task :lib-utils:preBuild UP-TO-DATE
监听【task ':lib-utils:preBuild'】beforeExecute
监听【task ':lib-utils:preBuild'】afterExecute

> Task :省略

> Task :lib-utils:bundleDebugAar UP-TO-DATE
监听【task ':lib-utils:bundleDebugAar'】beforeExecute
监听【task ':lib-utils:bundleDebugAar'】afterExecute
监听:buildFinished

BUILD SUCCESSFUL in 629ms

从上述日志可以看出:

  1. Gradle 生命周期大体分为:初始化阶段 -> 配置阶段 -> 执行阶段
  2. 配置阶段又大体分为:项目配置 -> 依赖配置 -> 任务执行顺序图配置

初始化阶段:

  1. 执行 /setting.gradle,这时候 ${project} 并不存在,打印的话直接编译错误
  2. 执行完成后回调:settingsEvaluated

配置阶段:项目配置 1. 这个阶段 ${project} 并已经存在了
2. 各项目执行顺序是:根目录项目配置(/build.gradle) -> 其他 module 项目配置(/module/build.gradle,这个配置顺序规则未知)
3. 整个项目配置前后回调:projectsLoaded、projectsEvaluated
4. 各项目配置前后回调:beforeEvaluate、afterEvaluate
5. 根目录项目配置有点特殊,先走 buildscript 的配置与依赖,然后才正常走 /build.gradle 文件其他内容,其中 allprojects 是每个项目都遍历一遍
6. 除了根目录项目配置,其他的 xxx.gradle 都是从上到下依次执行,即除了监听回调(如 doLast {}),其他的包括闭包,方法,任务等等会依次从上到下执行一次
7. 注意:写在各 module 项目中的 project.beforeEvaluate 是无效的,因为回调时机已经过去了

配置阶段:依赖配置 -> 任务执行顺序图配置

  1. 各项目依赖配置前后回调:beforeResolve、afterResolve,不过项却大于 module 项目数量,总之自行看日志
  2. 任务执行顺序图配置仅回调一次:TaskExecutionGraphListener

执行阶段:

  1. 开始按任务执行顺序图依次执行任务
  2. 任务执行前后回调:beforeExecute、afterExecute

Android 配置生命周期

没有啥生命周期可言,就是从上到下执行

Gradle 语法与常用 API

相关资料:

  • 下载的 Gradle docs 路径,以 6.6 为例
    • 用户手册 PDF:gradle-6.6/docs/userguide/userguide.pdf
    • 用户手册:gradle-6.6/docs/userguide/userguide.html
    • DSL 手册:gradle-6.6/docs/dsl/index.html
    • JavaDoc 手册:gradle-6.6/docs/javadoc/index.html
    • 发行说明(即版本迭代说明):gradle-6.6/docs/release-notes.html
  • Android Gradle 插件
    • developer.android.google.cn/reference/t…
    • 插件源码:Gradle 下载目录/caches/modules-2/files-2.1/com.android.tools.build/gradle/4.0.2/xxx/gradle-4.0.2-sources.jar
    • 其他相关源码:Gradle 下载目录/caches/modules-2/files-2.1/com.android.tools.build

语法糖:

  • ${project} 一定能用,而 $project 不一定能用
  • ${this},${super} 貌似等同于 ${project}
  • API 与属性的引用是可以省略的,至于到底省略的谁得根据上下文,如
    • API:在 build.gradle 中行首写 project.afterEvaluate{} 可以省略为 afterEvaluate{}
    • 属性:在 Task 的 doLast{ println "属性:${properties}" } 的完整写法是 doLast { task -> println "属性:${task.properties}" }

变量作用域

使用 def xxx = ??? 定义的变量

  • 在花括号中定义,则只在花括号中有效
  • 在 xxx.gradle 中定义,则只在该 gradle 文件中有效

使用 ext.xxx = 定义的变量

  • 类似类成员变量,在整个类对象中有效,与定义位置无关
  • 至于定义在哪个类对象,则看被省略的引用是哪个实例对象,如
    • 在 build.gradle 中定义 ext.custom01 = "custom01"ext { custom02 = "custom02" },则在任意地方,均可使用 project.custom01 属性
    • 在 Task 中定义 task myTask { ext.custom01 = "custom01" },则在任意地方,均可使用 myTask.custom01,注意这里仅 myTask 任务对象才能用
    • Android 项目:android { applicationVariants.all { variant -> ext.isTest = true } },则在任意地方,均可使用 variant.isTest(前提是能拿到 variant 对象)

自定义 Class

在 xxx.gradle 中任意地方定义 Class

class TestClass {
    String name
    String age
}

则关于作用域

  • 该 Class 只能在该 xxx.gradle 中文件中 new TestClass()
  • 关于 def custom01 = new TestClass()ext.custom02 = new TestClass() 见上个标题的《变量作用域》说明
    • 没错 ext 也是能用的,但只能访问与修改 custom02 的内容,即还是不能自由的 new TestClass()

那么如何自由的实例化 TestClass 对象呢?

方式一:使用放射的方式绕过去

project.ext.TestClass = TestClass

// 其他文件
def dto = project.TestClass.newInstance()
dto.name = "天才"

方式二:像 Java 那样自由的使用,待确认与补充

Project 相关

查看当前 project 的全部属性:

  • 方式一:运行任务【:lib-utils:properties】查看
  • 方式二:println "当前【${project}】项目的全部属性:${project.properties}"

常用综合:

// 属性相关
println "当前【${project}】项目的全部属性:${project.properties}"

// 监听
project.beforeEvaluate {
    println "监听【${project}】beforeEvaluate"
}
project.afterEvaluate {
    println "监听【${project}】afterEvaluate"
}

Task 相关

获取任务列表清单:运行任务【:lib-utils:tasks】

常用综合:

println "获取当前正在执行的任务列表(不包含依赖任务):${gradle.startParameter.taskRequests}"

// 属性相关
tasks.all { task ->
    println "当前【${task}】任务的全部属性:${task.properties}"
}

// 查找任务
Task taskFind = tasks.findByName("assembleDebug")
if (taskFind != null) {
    taskFind.doLast {
        println "查找到的指定任务【${task}】执行后打印"
    }
}

任务依赖、分组等

// 方式一
task01.dependsOn task02
task01.group 'upload'

// 方式二
task task01(group: 'upload', dependsOn: 'task02') {
}

参考文献与版本同步

参考文献:

博客同步版本:

  • 2021-08-23:首发博客