学新通技术网

【Gradle 学习】(二)构建工具 Gradle

juejin 13 1
【Gradle 学习】(二)构建工具 Gradle

1. Gradle 构建脚本

友情提示:本次关于 Gradle 的学习主要是为了学习在 Android Studio 上的使用,所以关于 Gradle 的下载安装就不做赘述,从官网下载,安装,配置变量即可。

每个 Gradle 构建包括三个基本的构建块:项目(projects)、任务(task)和 属性(properties)。每个构建至少包括一个项目,一个项目包括一个或多个任务,项目和任务都是有很多属性来控制构建过程。

1.1 项目 Project

每个 Gradle 构建都是以一个脚本开始的。Gradle 构建默认脚本的名字是 build.gradle。在执行 gradle 命令时,Gradle 会去寻找名为 build.gradle 的文件。

每个 Gradle 脚本至少定义一个 project。当开始构建过程后, Gradle 基于配置实例化 org.gradle.Project 类,并且能够通过 project 变量使其隐式可用。下图是 Project 类的 API 接口和重要的方法。

image.png

一个项目可以创建新的任务(task)、添加依赖(dependencies)和配置(configurations)、应用插件和其他脚本。很多属性比如 name 和 description 都可以通过 getter 和 setter 方法来访问。

Gradle 支持多项目构建,随着软件系统的复杂, 可以分解成一个个功能性模块,模块之间可以相互依赖,而每个模块也都有自己的独立的 build.gradle 脚本。

1.2 任务 Task

Task 是 独立原子性工作,一个动作就是任务的原子工作

很多时候一个任务需要在另一个任务之后执行,尤其是当一个任务的输入依赖于另一个任务的输出时,比如项目打包成 JAR 文件之前先要编译成 class 文件,来看下 Gradle API 中任务的表示:org.gradle.api.Task 接口。

Task.png

从 Hello World 开始了解,

在 build.gradle 中,定义一个独立的原子性工作,task。

task helloWorld {
    doLast {
        println("Groovy")
    }

    doFirst {
        print("Hello ")
    }
}

运行该 task:

$ gradle helloWorld
输出 :Hello Groovy

通过 -q 定义可选命令行选项 quiet,告诉 Gradle 只输出该 task 相关的信息

列出所有可用的 task gradle -q tasks

1.3 属性

每个 Project 和 Task 实例都提供了 setter 和 getter 方法来访问属性,比如:

task helloWorld {
    description = "This is a task."
    println(helloWorld.description)
}

1.4 外部属性

外部属性一般存储在键值对当中,要添加一个属性,需要用 ext 命名空间。

ext {
    aValue = 123
}

task helloWorld {
    println project.ext.aValue
}

运行任务会输出 123

外部属性也可以定义在一个属性文件当中。通过 /gradle 或者根目录下的 gradle.properties 文件来定义属性,这些属性可以通过 project 实例来访问。 注意/.gradle目录下只能有一个 Gradle 属性文件,哪怕是有多个项目。 在这份属性文件生命的属性对所有项目可用

在 gradle.properties 中定义个属性

testValue = 1234

在项目中可以访问这个变量

task helloWorld {
    println project.testValue
    println "test : $testValue"
}

1.5 依赖

Gradle DSL 配置 dependencies 用来给配置添加一个多或多个依赖,Gredle支持各种不同类型的依赖

类型 描述
外部模块依赖 依赖仓库中的外部类库,包括她所提供的元数据
项目依赖 依赖其他 Gradle 项目
文件依赖 依赖文件系统中的一系列文件
客户端模块依赖 依赖仓库中的外部类库,具有生命元数据的能力
Gradle 运行时依赖 依赖 Gradle API 或者封装 Gradle 运行时的类库

每个 Gradle 项目都有 DependencyHandler 接口实例, 可以通过 getDependencies() 方法来获取依赖处理器的引用。

表格中的每种依赖类型都是通过项目的 dependencies 配置块中的依赖处理器的方法来声明的。每个依赖都是 Dependency 类型的一个实例,group、name、version 和 classifier 这几个属性明确标识了一个依赖。

DependencyHandler.png

1.5.1 外部模块依赖

在 Gradle 中,外部类库通常以 Jar 文件形式存在,被称为外部模块依赖。表示依赖的模块在项目结构之外。其特点是在仓库中能够通过属性明确的标识。

dependencies {
  api 'io.reactivex.rxjava2:rxandroid:2.1.0'
}

Gradle 不会指定默认的仓库,要使用这个类库还要指定使用的仓库,下面代码块表示使用 Maven Central 下载依赖。

repositories {
  mavenCentral()
}

动态版本声明

如果想使用一个依赖的最新版本,可以使用占位符 last.integration,比如声明 Rxjava 的最新版本

dependencies {
  api 'io.reactivex.rxjava2:rxandroid:latest-integration'
}

或者使用一个 + 号来动态声明版本

dependencies {
  api 'io.reactivex.rxjava2:rxandroid:2.+'
}

不过动态版本最好少用或者不用,可靠的和可重复的构建是最重要的。选择新版本的类库可能会导致构建失败。更糟糕的是,可能在不知情的情况下,引入了不兼容的类库版本和副作用,这样也很难知道原因。所以,声明确切类库版应该成为惯例。

1.5.2 文件依赖

文件依赖也很常见,比如我们把一个 JAR 包放在 lib 目录下,就是文件依赖

dependencies {
  api fileTree(include: ['*.jar'], dir: 'libs')
}

3.5.3 项目依赖

在多项目构建时,可以声明项目依赖,配置 compile 是 Java 插件提供的,比如下面代码块,声明编译时依赖的项目

dependencies{
    complie project(':model')
}

1.6 仓库配置

Gradle 支持以下仓库类型

  • Maven 仓库,本地文件系统或远程服务器中的 Maven 仓库,或者预配置的 Maven Central
  • lvy 仓库,本地文件系统或远程服务器中的 Ivy 仓库,具有特定的结构模式
  • 扁平的目录仓库,本地文件系统中的仓库

定义仓库的关键是 RepositoryHandler 接口,它提供了添加各种类型仓库的方法,这些方法会在 repositories 配置块中调用,可以声明多个仓库。

image.png 在 Android 中 这些仓库也并不少见

1.6.1 Maven 仓库

Maven 仓库是 Java 项目中最常用的仓库类型之一,在构建脚本声明一个依赖时,其属性可以用来获取它在仓库的确切位置。(Maven 仓库)

image.png RepositoryHandler 接口提供了两个方法运行第一预配置的 Maven 仓库 mavenCentral() 和 mavenLocal()

  • Maven Central 仓库,使用 mavenCentral() 方法用来将 Maven Central 引用添加到一系列仓库中

  • 本地 Maven 仓库,使用 mavenLocal() 方法用来在文件系统中关联一个本地的 Maven() 仓库

  • 添加自定义的 Maven 仓库

    repositories {
        mavenCentral()
        mavenLocal()
        maven {
          name 'Custom Maven Repository',
            url 'http://repository.forge.cloudbees.com/release/')
        }
    }
    

    mvnrepository.com/repos

2. 多项目构建

比如在 Android 的开发中也会用X模块化结构来分离业务逻辑,每一个模块都可以作为一个单独的项目来看待。在多项目构建中是通过 settings 文件来声明子项目(模块)的。

2.1 Settings 文件

settings 文件声明了所需的配置来实例化项目的层次结构。在默认情况下,这个文件被命名为 settings.gradle,并且和 build.gradle 文件放在一起。

若想使每个子项目都成为构建的一部分,可以调用带有项目路径参数的 include 方法。比如

//参数是项目路径,不是文件路径
include 'model'
include 'repository', 'web'

上面代码中所提到的项目路径是相对于根目录的项目目录。也可以构建更深层次的项目结构。使用冒号(:)来分隔每个子项目的层次结构。

include ':app'

Settings API

image.png

我们也常用这样的写法来指定模块的目录

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

2.2 子项目配置

在前面介绍了一些 Project API 的属性和方法,同时这个 API 为实现多项目构建也提供了相关的方法。

image.png

Project API 提供了 allprojects 和 subprojects 方法来定义所有项目或者只有子项目的一些逻辑。

比如:

image.png

你可以用 allprojects 方法给所有的项目添加属性,由于根项目不需要 Java 插件,你可以使用 subprojects 给所有子项目添加 Java 插件,如下所示:

 allprojects {
        group = 'com.manning.gia'
        version = '0.1'
    }
​
    subprojects {
        apply plugin: 'java'
    }
​
    project(':repository') {
        dependencies {
            compile project(':model')
        }
    }
​
    project(':web') {
        apply plugin: 'war'
        apply plugin: 'jetty'
​
        repositories {
            mavenCentral()
        }
​
        dependencies {
            compile project(':repository')
            providedCompile 'javax.servlet:servlet-api:2.5'
            runtime 'javax.servlet:jstl:1.1.2'
        }
    }

2.3 子项目的配置文件

如果多项目构建只有一个 build.gradle 文见 和 settings.gradle,随着项目中的子项目越来越多,那么构建文件也越来越难维护,所以可以为每个项目创建单独的 build.gradle

根目录的 build.gradle 只保留公共配置块即可

allprojects {
    group = 'com.manning.gia'
    version = '0.1'
}
subprojects {
    apply plugin: 'java'
}

repository 子项目的构建代码:

dependencies {
    compile project(':model')
}

3. Gradle 包装器

Gradle Wrapper

只能在自己的机器跑?

一个项目使用了 Gradle,在其他人使用这个项目的时候,还要安装 Gradle。不同的 Gradle 版本有不同特性,还要询问你安装了哪个版本的 Gradle,怎么运行, Mac 和 Windows 安装有什么区别。因为在本机上运行成功在其他机器上运行失败,太平常了。

针对这个问题,Gradle 提供了一个方便而使用的解决方案,就是 Gradle 包装器。它能够让机器在没有安装 Gradle 运行的情况下运行 Gradle 构建(PS:在使用 AS 的时候,我们没有专门安装过 Gradle 吧,就是因为有这个 Gradle Wrapper)

Gradle 包装器让构建脚本运行在一个指定的 Gradle 版本上。它在中心仓库自动下载 Gradle 运行的时候,解压再构建。 目标就是创建可靠的、可复用的、与操作系统、系统配置或 Gradle 版本无关的构建。

3.1 配置包装器

配置包装器,只需做两件事:创建一个包装器 和 执行任务生成包装器文件 ! image.png

gradleVersion 指定了想要使用的 Gradle 版本,任务执行之后,会在构建脚本同级目录下看到包装器文件

image.png

(PS:这个层级看起来也很眼熟,没错,AS 里面默认使用 Gradle 构建,这些都为我们做好了。)

包装器提供了名为 gradlew 的脚本,使用他们运行构建和使用已安装好的 Gradle 运行构建是一样的。

  • gradle-wrapper.jar: Gradle Wrapper 的主体功能包,包含下载 Gradle 版本的代码。
  • gradle-wrapper.properties: Wrapper 运行时的配置文件,文件主要指定了该项目需要从哪里下载什么版本的 Gradle,下载后放到哪里。
  • gradlew,gradlew.bat :使用 Wrapper 执行构建的 shell 脚本 和 Windows 批处理文件

3.2 Wrapper 的工作流程

Wrapper 的工作流程

image.png 在我们使用 Gradle 构建的时候,如果没有 Gradle 那么先下载 Gradle 的发行版(从 distributionUrl 这个地址去下载,下载到指定路径 Base+Path)中,下载完成之后,再进行 Gradle 构建直接使用即可。再来看 gradle-wrapper.properties 这个文件下的几个属性就很清楚了

gradle-wrapper.properties 文件

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-4.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
  • distributionBase:Gradle解包后存储的主目录
  • distributionPath:distributionBase指定目录的子目录

    distributionBase+distributionPath 就是 Gradle 解包后的存放位置

  • zipStoreBase:Gradle压缩包存储主目录
  • zipStorePath:zipStoreBase指定目录的子目录

    zipStoreBase+zipStorePath 就是 Gradle 压缩包的存放位置

  • distributionUrl:要下载的 gradle 地址以及版本,gradle-wrapper 会先去 wrapper/dists 目录去找,如果没有对应的版本就会下载。

在 IDEA 的 Settings 有配置根目录 ,所以下载的 Gradle 版本一般在用户目录下的.gradle/wrapper/dists 存放

image.png

总结

到此上面介绍了 Gradle 构建脚本概要、依赖管理以及多项目构建的脚本配置方式。以及可以代替Gradle 执行命令的 Gradle 包装器。

后面会介绍 Gradle 在 Android 中的使用。

本文出至:学新通技术网

标签: