Gradle基础篇

作者: 小村医 | 来源:发表于2019-08-03 09:27 被阅读9次

项目和任务

Gradle中的所有内容都基于两个基本概念:项目任务

每个Gradle构建都由一个或多个项目组成。项目代表什么取决于您使用Gradle做什么。例如,项目可能表示JAR或Web应用程序。

每个项目由一个或多个任务组成。任务代表构建执行的一些原子工作。这可能是编译某些类,创建JAR,生成Javadoc或将一些存档发布到存储库。

Hello,World

您使用gradle命令运行Gradle构建。gradle命令查找当前目录中的build.gradle文件。我们称这个build.gradle文件为构建脚本,但严格来说它是一个构建配置脚本

task hello {
    doLast {
        println 'Hello world!'
    }
}

命令行shell中,执行gradle -q hello

n$ gradle -q hello
Hello world!

任务依赖

您可以声明依赖于其他任务的任务。

task hello {
    doLast {
        println 'Hello world!'
    }
}
task intro {
    dependsOn hello
    doLast {
        println "I'm Gradle"
    }
}

输出

$ gradle -q intro
Hello world!
I'm Gradle

依赖尚不存在的任务

task taskX {
    dependsOn 'taskY'
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
$ gradle -q taskX
taskY
taskX

可以在定义之前声明taskX 的依赖关系。这种自由对于多项目构建非常重要。

动态任务

您还可以使用动态创建任务。

4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}
$ gradle -q task1
I'm task number 1

您可以在运行时动态地向任务添加依赖项

4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}
task0.dependsOn task2, task3
$ gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0

或者,您可以向现有任务添加行为。

task hello {
    doLast {
        println 'Hello Earth'
    }
}
hello.doFirst {
    println 'Hello Venus'
}
hello.configure {
    doLast {
        println 'Hello Mars'
    }
}
hello.configure {
    doLast {
        println 'Hello Jupiter'
    }
}
$ gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter

doFirstdoLast可以执行多次。他们将操作添加到任务的操作列表的开头或结尾。执行任务时,将按顺序执行操作列表中的操作。

Groovy DSL快捷方式表示法

有一种方便的表示法来访问现有任务。每个任务都可以作为构建脚本的属性使用:

将任务作为构建脚本的属性进行访问

task hello {
    doLast {
        println 'Hello world!'
    }
}
hello.doLast {
    println "Greetings from the $it.name task."
}
$ gradle -q hello
Hello world!
Greetings from the hello task.

默认任务

Gradle允许您定义在未指定其他任务时执行的一个或多个默认任务。

defaultTasks 'clean', 'run'

task clean {
    doLast {
        println 'Default Cleaning!'
    }
}

task run {
    doLast {
        println 'Default Running!'
    }
}

task other {
    doLast {
        println "I'm not a default task!"
    }
}
$ gradle -q
Default Cleaning!
Default Running!

gradle 项目分析

├── app
│   ├── app.iml
│   ├── build.gradle
│   ├── libs
│   ├── proguard-rules.pro
│   └── src
│       ├── main
│       │   ├── AndroidManifest.xml
│       │   ├── java
│       │   │   └── com
│       │   │       └── binzi
│       │   │           └── gradle
│       │   │               └── MainActivity.java
│       │   └── res
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.iml
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle

settings.gradle

settings.gradle 是负责配置项目的脚本
对应 Settings类,gradle 构建过程中,会根据 settings.gradle 生成 Settings 的对象
其中几个主要的方法有:

  • include(projectPaths)
  • includeFlat(projectNames)
  • project(projectDir)

一般在项目里见到的引用子模块的方法,就是使用 include

include ':app'

如果想指定子模块的位置,可以使用 project 方法获取 Project 对象,设置其 projectDir 参数

include ':app'
project(':app').projectDir = new File('./app')

rootproject/build.gradle

build.gradle 负责整体项目的一些配置,对应的是 Project
gradle 构建的时候,会根据 build.gradle 生成 Project 对象,所以在 build.gradle 里写的 dsl,其实都是 Project 接口的一些方法,Project 其实是一个接口,真正的实现类是 DefaultProject
其中几个主要方法有:

  • buildscript // 配置脚本的 classpath
  • allprojects // 配置项目及其子项目
  • respositories // 配置仓库地址,后面的依赖都会去这里配置的地址查找
  • dependencies // 配置项目的依赖

module/build.gradle

build.gradle 是子项目的配置,对应的也是 Project 类
子项目和根项目的配置是差不多的,不过在子项目里可以看到有一个明显的区别,就是引用了一个插件 apply plugin "com.android.application",后面的 android dsl 就是 application 插件的 extension

其中几个主要方法有:

  • compileSdkVersion // 指定编译需要的 sdk 版本
  • defaultConfig // 指定默认的属性,会运用到所有的 variants 上
  • buildTypes // 一些编译属性可以在这里配置,可配置的所有属性在 这里
  • productFlavor // 配置项目的 flavor

依赖

在 gradle 3.4 里引入了新的依赖配置,如下:

新配置 弃用配置 行为 作用
implementation compile 依赖项在编译时对模块可用,并且仅在运行时对模块的消费者可用。 对于大型多项目构建,使用 implementation 而不是 api/compile 可以显著缩短构建时间,因为它可以减少构建系统需要重新编译的项目量。 大多数应用和测试模块都应使用此配置。 api 只会暴露给直接依赖的模块,使用此配置,在模块修改以后,只会重新编译直接依赖的模块,间接依赖的模块不需要改动
api compile 依赖项在编译时对模块可用,并且在编译时和运行时还对模块的消费者可用。 此配置的行为类似于 compile(现在已弃用),一般情况下,您应当仅在库模块中使用它。 应用模块应使用 implementation,除非您想要将其 API 公开给单独的测试模块。 api 会暴露给间接依赖的模块,使用此配置,在模块修改以后,模块的直接依赖和间接依赖的模块都需要重新编译
compileOnly provided 依赖项仅在编译时对模块可用,并且在编译或运行时对其消费者不可用。 此配置的行为类似于 provided(现在已弃用)。 只在编译期间依赖模块,打包以后运行时不会依赖,可以用来解决一些库冲突的问题
runtimeOnly apk 依赖项仅在运行时对模块及其消费者可用。 此配置的行为类似于 apk(现在已弃用)。 只在运行时依赖模块,编译时不依赖

gradle wrapper

gradlew / gradlew.bat 这个文件用来下载特定版本的 gradle 然后执行的,就不需要开发者在本地再安装 gradle 了。这样做有什么好处呢?开发者在本地安装 gradle,会碰到的问题是不同项目使用不同版本的 gradle 怎么处理,用 wrapper 就很好的解决了这个问题,可以在不同项目里使用不同的 gradle 版本。gradle wrapper 一般下载在 GRADLE_CACHE/wrapper/dists 目录下

gradle/wrapper/gradle-wrapper.properties 是一些 gradlewrapper 的配置,其中用的比较多的就是 distributionUrl,可以执行 gradle 的下载地址和版本
gradle/wrapper/gradle-wrapper.jar 是 gradlewrapper 运行需要的依赖包

gradle 生命周期

gradle 构建分为三个阶段
初始化阶段

在初始化阶段,Gradle判断需要参与编译的工程,为每个工程创建一个Project对象。在这个阶段,Gradle会创建Settings对象,并在其上执行settings.gradle脚本,建立工程之间的层次关系。

配置阶段

在这个阶段,Gradle会分别在每个Project对象上执行对应的build.gradle脚本,对Project进行配置。

执行阶段

在执行阶段,Gradle会判断配置阶段创建的哪些Task需要被执行,然后执行选中的每个Task。

gradle 在构建过程中,会提供一些列回调接口,方便在不同的阶段做一些事情,主要的接口有下面几个:

gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {
        println('构建开始')
        // 这个回调一般不会调用,因为我们注册的时机太晚,注册的时候构建已经开始了,是 gradle 内部使用的
    }

    @Override
    void settingsEvaluated(Settings settings) {
        println('settings 文件解析完成')
    }

    @Override
    void projectsLoaded(Gradle gradle) {
        println('项目加载完成')
        gradle.rootProject.subprojects.each { pro ->
            pro.beforeEvaluate {
                println("${pro.name} 项目配置之前调用")
            }
            pro.afterEvaluate{
                println("${pro.name} 项目配置之后调用")
            }
        }
    }

    @Override
    void projectsEvaluated(Gradle gradle) {
        println('项目解析完成')
    }

    @Override
    void buildFinished(BuildResult result) {
        println('构建完成')
    }
})

gradle.taskGraph.whenReady {
    println("task 图构建完成")
}
gradle.taskGraph.beforeTask {
    println("每个 task 执行前会调这个接口")
}
gradle.taskGraph.afterTask {
    println("每个 task 执行完成会调这个接口")
}

常用API

org.gradle.api.Project

Project对象是Gradle中最核心的API,通过Project对象可以访问所有Gradle特性。

Project与build.gradle

Project对象和build.gradle文件一一对应。在Gradle构建时,会先创建Settings实例并在其上执行settings.gradle;再通过Settings对象定义的Project层级,创建若干个Project实例,并分别在其上执行对应的build.gradle

Extra属性

Project有一个Extra属性,可通过ext前缀在其中定义属性,定义好后可以不加ext前缀直接访问。

project.ext.prop1 = "foo"
task doStuff {
    ext.prop2 = "bar"
}
ext.isSnapshot = version.endsWith("-SNAPSHOT")
if (isSnapshot) {
    // do snapshot stuff
}

Project的属性/方法调用

build.gradle中调用属性,或调用Project.property(java.lang.String)方法时,会按顺序从以下范围查找:

  1. Project自身定义的属性
  2. Project的Extra属性
  3. 插件添加的Extension属性
  4. 插件添加的Convension属性
  5. Project中Task的名字
  6. 从父Project继承的属性,一直递归到RootProject

build.gradle中调用方法时,会按顺序从以下范围查找:

  1. Project自身定义的方法
  2. build.gradle脚本定义的方法
  3. 插件添加类型为Action或Closure的Extension
  4. 插件添加的Convension方法
  5. Project中Task的名字都会创建一个对应方法
  6. 从父Project继承的方法,一直递归到RootProject
  7. Project中为Closure类型的属性可以作为方法调用

Project继承了PluginAwareExtensionAware,分别用于支持Plugin和Extension方法。部分常用API如下。

public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
    Project getRootProject();
    File getRootDir();
    File getBuildDir();
    void allprojects(Closure configureClosure);
    ScriptHandler getBuildscript();
    void buildscript(Closure configureClosure);
    RepositoryHandler getRepositories();
    void repositories(Closure configureClosure);
    ConfigurationContainer getConfigurations();
    void configurations(Closure configureClosure);
    DependencyHandler getDependencies();
    void dependencies(Closure configureClosure);
    ConfigurableFileCollection files(Object... paths);
    ConfigurableFileTree fileTree(Object baseDir);
    Convention getConvention();
    ExtensionContainer getExtensions();
    Task task(String name) throws InvalidUserDataException;
    Task task(String name, Closure configureClosure);
    void afterEvaluate(Closure closure);
    // ...
}

常用API示例(以下脚本均写在build.gradle中):

// 配置Gradle插件,闭包参数会在ScriptHandler上执行
buildscript {
// ...
}
// 配置所有工程,闭包参数会分别在每个Project上执行
allprojects {
// ...
}
// 配置使用的仓库,闭包参数会在RepositoryHandler上执行
repositories {
// ...
}
// 配置依赖项,闭包参数会在DependencyHandler上执行。
// files和fileTree也是Project提供的API,
// 而project则是DependencyHandler提供的API。
dependencies {
compile files('hibernate.jar', 'libs/spring.jar')
compile fileTree('libs')
    compile project(path: ':library')
    // ...
}
// 在当前Project配置完成后,闭包会被执行
afterEvaluate {
    println "Project '$name' has been evaluated!"
}
// 在RootProject配置完成后,闭包会被执行
rootProject.afterEvaluate {
    println "RootProject '$name' has been evaluated!"
}

org.gradle.api.invocation.Gradle

Gradle对象表示一次Gradle调用,通过Project.getGradle()可以获取这个对象。在一次构建过程中只有一个Gradle对象。

public interface Gradle extends PluginAware {
    String getGradleVersion();
    File getGradleUserHomeDir();
    File getGradleHomeDir();
    Gradle getParent();
    Project getRootProject() throws IllegalStateException;
    void rootProject(Action< ? super Project> action);
    void allprojects(Action< ? super Project> action);
    TaskExecutionGraph getTaskGraph();
    StartParameter getStartParameter();
    ProjectEvaluationListener addProjectEvaluationListener(ProjectEvaluationListener listener);
    void removeProjectEvaluationListener(ProjectEvaluationListener listener);
    void beforeProject(Closure closure);
    void afterProject(Closure closure);
    void buildStarted(Closure closure);
    void settingsEvaluated(Closure closure);
    void projectsLoaded(Closure closure);
    void projectsEvaluated(Closure closure);
    void buildFinished(Closure closure);
    void addBuildListener(BuildListener buildListener);
    public void addListener(Object listener);
    public void removeListener(Object listener);
    public void useLogger(Object logger);
    Gradle getGradle();
}

org.gradle.api.initialization.Settings

Settings对象主要用于配置Project的层级结构。

Settings对象和settings.gradle文件一一对应。Gradle构建的第一步,就是创建Settings对象并其上执行settings.gradle脚本。

public interface Settings extends PluginAware {
    String DEFAULT_SETTINGS_FILE = "settings.gradle";
void include(String[] projectPaths);
void includeFlat(String[] projectNames);
    Settings getSettings();
File getSettingsDir();
    File getRootDir();
    ProjectDescriptor getRootProject();
    ProjectDescriptor project(String path) throws UnknownProjectException;
    ProjectDescriptor findProject(String path);
    ProjectDescriptor project(File projectDir) throws UnknownProjectException;
    ProjectDescriptor findProject(File projectDir);
    StartParameter getStartParameter();
    Gradle getGradle();
}

常用API示例:

  1. include()可以配置包含Project,例如include ':app', ':library'

  2. project()可获取ProjectDescriptor从而做一些配置,例如经常会配置Gradle依赖本地Library工程的路径:

    include ':img:library'project(':img:library').projectDir = new File('../../img/library')
    

org.gradle.api.Task

Task

Task也是Gradle中很重要的API。Task代表构建过程中的一个原子操作,例如编译classes文件或生成JavaDoc。

每个Task属于一个Project。每个Task都有一个名字。所属Project名+Task名可组成唯一的完整名(fully qualified path),例如:app:assemble

Action

每个Task包含一个Action序列,并在Task执行时按先后顺序执行。通过Task的doFirst/doLast方法可以往Action序列的头部/末尾添加Action,支持Action或闭包(闭包会被转换成Action对象)。

Task依赖和排序

每个Task可以依赖其他Task,执行Task时会先执行其依赖的Task,通过dependsOn可设置依赖。每个Task还可以设置在其他Task之前、之后执行,一般可通过mustRunAfter设置。

例如下面的配置,执行A时一定会先执行B;执行A不一定会执行C;当A、C都要执行时一定先执行C。

taskA.dependsOn(taskB)
taskA.mustRunAfter(taskC)

Task的部分常用API如下:

public interface Task extends Comparable<Task>, ExtensionAware {
    String getName();
    Project getProject();
    TaskDependency getTaskDependencies();
    Task dependsOn(Object... paths);
    String getPath();
    Task doFirst(Action< ? super Task> action);
    Task doFirst(Closure action);
    Task doLast(Action< ? super Task> action);
    Task doLast(Closure action);
    Task configure(Closure configureClosure);
    Task mustRunAfter(Object... paths);
    TaskDependency shouldRunAfter(Object... paths);
    // ...
}

Task 的一些重要方法分类如下:

  • Task 行为
    Task.doFirst
    Task.doLast
  • Task 依赖顺序
    Task.dependsOn
    Task.mustRunAfter
    Task.shouldRunAfter
    Task.finalizedBy
  • Task 的分组描述
    Task.group
    Task.description
  • Task 是否可用
    Task.enabled
  • Task 输入输出
    gradle 会比较 task 的 inputs 和 outputs 来决定 task 是否是最新的,如果 inputs 和 outputs 没有变化,则认为 task 是最新的,task 就会跳过不执行
    Task.inputs
    Task.outputs
  • Task 是否执行
    可以通过指定 Task.upToDateWhen = false 来强制 task 执行
    Task.upToDateWhen

Task创建

注:Gradle不推荐使用task hello << { ... }的方式定义Task,并会在后续版本删除,因此这里不做介绍。

build.gradle中创建Task,最常见写法如下。task(xxx)是Project提供的API,最终调用了TaskContainer的create方法。可接收参数包括:

  • Task名称(必选)
  • Map<String, ?>类型配置(可选)
  • 闭包配置(可选)
task hello(dependsOn: clean) {
doLast {
    println 'hello'
    }
}

也可以直接调用TaskContainer创建Task,Project中的tasks属性即为TaskContainer对象。

tasks.create('hello')

Task创建后会在Project上添加一个同名方法,调用这个方法可以配置Task。

task hello
hello {
doLast {
    println 'hello'
    }
}

Task的type属性,带参数的Task

还可以用类实现Task,创建Task时指定type为这个class即可,定义Task的类通常继承自DefaultTask。下列示例代码中给Task定义了一个名为name的参数。

import org.gradle.api.internal.tasks.options.Option
class HelloTask extends DefaultTask {
    String personName = '';
    HelloTask() {
        doLast {
            println "Hello " + personName
        }
    }
    @Option(description = "set person name", option = "name")
    def setMessage(String name) {
        this.personName = name;
    }
}
task hello(type: HelloTask)

命令行中执行效果:

$ ./gradlew hello --name Tom
:hello
Hello Tom
BUILD SUCCESSFUL
Total time: 0.889 secs

相关文章

网友评论

    本文标题:Gradle基础篇

    本文链接:https://www.haomeiwen.com/subject/qcgddctx.html