Gradle
安装
准备阶段
Gradle 需要运行在一个 Java 环境里,因此安装Gradle前需要先安装JDK
安装一个 Java JDK 或者 JRE. 而且 Java 版本必须至少是 6 以上. Gradle 自带 Groovy 库,
所以没必要安装 Groovy. 任何已经安装的 Groovy 会被 Gradle 忽略.
下载
- 下载: 你可以从 Gradle网站 下载任意一个已经发布的版本
- 解压后大致可以看到以下内容:
- Gradle 的二进制文件.
- 用户指南 (HTML 和 PDF).
- DSL参考指南.
- API文档 (Javadoc和 Groovydoc).
- 扩展的例子,包括用户指南中引用的实例,以及一些更复杂的实例来帮助用户构建自己的build.
- 二进制源码.此代码仅供参考.
设置环境变量
- 添加一个 GRADLE_HOME 环境变量来指明 Gradle 的安装路径
- 添加 GRADLE_HOME/bin 到您的 PATH 环境变量中. 通常, 这样已经足够运行Gradle了.
扩充教程
Max OX: 假设您下载好的 Gradle 文件在 /Users/UFreedom/gradle 目录
1. vim ~/.bash_profile2.添加下面内容: export GRADLE_HOME = /Users/UFreedom/gradle export export PATH= PATH: GRADLE_HOME/bin
3.source ~/.brash_profile 实际配置中,您需要将上面的目录换成您的 Gradle 文件在系统中的目录.
运行并测试您的安装
我们可以通过在控制台输入 gradle 命令来运行Gradle. 通过 gradle -v 命令来检测Gradle是否已经正确安装. 如果正确安装,会输出Gradle版本信息以及本地的配置环境 ( groovy 和 JVM 版本等). 显示的版本信息应该与您所下载的 gradle 版本信息相匹配. 如:
Gradle Wrapper
为什么需要Gradle Wrapper
- 有些软件(应用)需要设备上先安装Gradle才能正常使用(如Android Studio,下文简称AS),因此为了避免这种情况,Gradle提供了一个Wrapper,使得Gradle可以像是被集成在AS中一样(只要安装AS即可)。
- 另外还考虑到一种情况:我们不能确保设备上已有的Gradle版本适用于我们需要使用的软件。有可能Gradle的版本过老而导致和下载的软件版本不兼容。
因此Gradle Wrapper即可认为是对Gradle进行封装,简单的理解(Gradle Wrapper = Gradle + 相关的集成、运行脚本文件)。
使用Wrapper
Gradle Wrapper的使用与Gradle略微不同,他的指令为:
- ./gradlew (on Unix-like platforms such as Linux and Mac OS X)
- gradlew (on Windows using the gradlew.bat batch file)
这里需要说明的是,Gradle Wrapper初始状态是不包含Gradle文件,而是以配置的方式让你可以方便的进行操作。如配置Gradle的下载路径:
distributionUrl = https\://services.gradle.org/distributions/gradle-3.1-all.zip
当第一次执行gradlew命令是,会检查本地是否存在对应的Gradle版本,如果不存在则会先下载对应版本。这个在“Gradle在Android中的应用”中的Gradle版本升级中提到过。
Gradle能做什么(命令行)
Gradle的安装我们在前面的章节提到过了,用gradle -v确认安装成功就可以继续接下来的工作了。
先看看Gradle自带哪些功能
使用gradle tasks可以看到,Gradle自带的(目前可用的)Task基本都是一些类似帮助使用的。
Gradle初始化
新建一个Hello Gradle目录(D:\Hello Gradle),然后cmd窗口进入到改目录,先执行gradle init命令,可以看到自动生成Gradle相关的文件即目录,如下:
(.gradle = gradle脚本缓存文件 )
help
我们可以通过gradle -h命令来查看,在执行task命令时还可以做哪些特定的操作
任务(Task)的定义及使用
我们在HelloGradle\build.gradle中添加一下内容:
task compile << {
println 'compiling source'
}
task compileTest(dependsOn: compile) << {
println 'compiling unit tests'
}
task test1(dependsOn: [compile, compileTest]) << {
println 'running unit tests'
}
task dist(dependsOn: [compile, test1]) << {
println 'building the distribution'
}
这里有个重要的概念,就是这个dependsOn,如我们执行compileTest时,会先执行依赖的compile这个Task。这个在任务的定义及使用过程中非常常见且重要。
现在我们看一下执行gradle dist这个任务的结果:
由于每个任务仅会被调用一次,所以调用gradle test test与调用gradle test效果是相同的.
help中的指令在运行Task时的应用
格式: gradle [option…] [task…]
- -x, –exclude-task
你可以用命令行选项-x来排除某些任务,如gradle dist -x test1,去除test1 - -t, –continuous
默认情况下, 只要有任务调用失败, Gradle就会中断执行. 这可能会使调用过程更快, 但那些后面隐藏的错误就没有办法发现了. 所以你可以使用 –continue 选项在一次调用中尽可能多的发现所有问题.
采用 –continue 选项, Gralde 会调用每一个任务以及它们依赖的任务. 而不是一旦出现错误就会中断执行.所有错误信息都会在最后被列出来. - -b, –build-file
调用 gradle 命令时, 默认情况下总是会构建当前目录下的文件, 可以使用 -b 参数选择其他目录的构建文件(并且当你使用此参数时 settings.gradle 将不会生效) - -p, –project-dir
参数用以指定脚本目录即可. - -d, –deubg
打印执行过程中log信息 - -q, –quiet
只打印错误(error)信息 –profile
参数可以收集一些构建期间的信息(如构建时间)并保存到 build/reports/profile 目录下. 并且会以构建时间命名这些文件.最后,说一个比较重要的点,Gradle执行任务时,可以简化任务名,比如我们的dist任务(Task),就可以使用 gradle di,或者驼峰式的,如执行compileTask:gradle cT
Gui
黑窗口(cmd)在交互体验上难免欠缺,所以Gradle也是提供了图形化界面,用gradle -gui启动
Task tree
如果我们从自己的Gradle项目目录中启动这个图形界面,我们应该会看到任务树。可以看到我们之前定义的4个任务:
Favorites
就是一个命令的收藏夹。
Command Line
将命令填入到gradle输入框. 就可以直接执行单个的Gradle命令.
Setup
设置界面
- 列表内容“Current Directory”
图形界面会默认设置您的Gradle项目的根目录(build.gradle 文件所在的目录)为当前目录. - “Stack Trace Output“
这个选项可以指定当错误发生时,有多少信息可以写入到轨迹栈中,注意:在您设定轨迹栈级别后,如果”Command Line”(命令行)选项卡中,或者在”Favorites”(收藏夹)选项卡中的命令发生错误,这个设置就不会起作用了. - ”Only Show Output When Errors Occur”
设定当编译出问题时输出窗口才显示相关信息. - “Use Custom Gradle Executor”
高级功能,您可以指定一个路径启动Gradle命令代替默认的设置,例如您的项目需要在别的批处理文件或者shell脚本进行额外的配置(例如指定一个初始化脚本),这种情况您就可以使用它.
创建一个Java项目
该章节,准备用 Gradle 来构建运行 hello world 程序,体会一下不用任何 IDE ,只用 Gradle (构建工具)是咋回事,然后我们再继续核心概念的介绍。 为了方便起见,我们选择JAVA项目而不是Android项目。
创建一个新项目
由上个章节可以看出,Gradle init的目录结构还不是我们想要的Java的结构,其实也比较好理解,我们知道Gradle是独立存在的,所以要创建Java项目,我们必须借用Java Plugin(关于插件的内容后面将会介绍)。
清空目录下的内容(或新建一个空目录),然后使用命令gradle init –type java-library,这里我们指定了类型为Java,因此初始化完我们可以看到目录结构的变化:
我们可以看到多了一个src目录,以及build.gradle文件的变化(之前的文件是个空内容):
// Apply the java plugin to add support for Java
apply plugin: 'java'
// In this section you declare where to find the dependencies of your project
repositories {
// Use 'jcenter' for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
// In this section you declare the dependencies for your production and test code
dependencies {
// The production code uses the SLF4J logging API at compile time
compile 'org.slf4j:slf4j-api:1.7.21'
// Declare the dependency for your favourite test framework you want to use in your tests.
// TestNG is also supported by the Gradle Test task. Just change the
// testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
// 'test.useTestNG()' to your build script.
testCompile 'junit:junit:4.12'
}
编辑java文件,并编译
我们可以看到在src/main/java下已经自动生成一个Library.java的文件,暂时不管这个。新建一个Main.java,并让他打印一句“Hello World”,保存后运行gradle build进行编译(构建):
//Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
可以看到多了一个build目录,该目录下存放的即为gradle构建完产生的Main.class文件、HelloGradle.jar以及一些report文件等。
运行项目
为了可以正常运行项目,需要在build.gradle里面添加插件application ,并且制定入口(mainClassName),然后执行gradle run即可,结果见下图:
小结
以上步骤,我们完全依靠Gradle(及相关插件)便完成了项目创建、编译、运行的过程。可以看到:
1. Gradle插件非常重要,正如我们之前提到的Gradle的设计理念“Gradle只负责一些最基础的任务,而特性化的功能都交于插件来封装与实现。”
2. 这个过程Gradle做的工作大概有:利用JDK将.java编译(javac)成.class,最终根据指定的入口运行(java).class文件。
3. 当然Gradle还有很多重要的功能,其中非常重要的依赖管理,将在后面进行介绍。
配置
我们可以同过添加gradle.properties文件来给Gradle添加一些附加配置。gradle.properties可以在项目目录下(针对当前项目),或者 gradle user home((window)/Users/”your pc_name”/.gradle)目录下。我们在说到统一依赖管理的时候有提到这个文件,可以利用他进行一些全局性的属性配置。先看几个Gradle自带的属性:
Gradle Daemon
我们可以通过使用Dradle Daemon[‘diːmən]来减少Gradle的启动时间,他是一个后台常驻进程(3个小时不被调用会自动结束),会在你下一次执行builds之前的空闲时间中做一些预处理工作,如缓存项目结构、文件等信息。 Gradel Daemon将在Gradle3.0+的版本中默认开启,也可以在项目的gradle.properties中添加:
org.gradle.daemon = trueParallel Project Execution
当你项目中有多个子module时,这个配置显得尤为重要,同样可以在项目的gradle.properties中添加:
org.gradle.parallel = trueConfigure projects on demand
按需配置(Configuration ondemand)只对任务相关的项目进行配置,这在大型多项目编译过程中非常有用,能够大幅度的减少不必要的配置时间。
附:提高构建速度的配置
# The Gradle daemon aims to improve the startup and execution time of Gradle.
# When set to true the Gradle daemon is to run the build.
#我们可以通过使用Dradle Daemon来减少Gradle的启动时间,他是一个后台常驻进程,会在你下一次执行builds之前的空闲时间中做一些预处理工作,
#如缓存项目结构、文件等信息。Gradel Daemon将在Gradle3.0+的版本中默认开启
org.gradle.daemon=true
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
#http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
#(并行模式)当你项目中有多个子module时,这个配置显得尤为重要
org.gradle.parallel=true
# Enables new incubating mode that makes Gradle selective when configuring projects.
# Only relevant projects are configured which results in faster builds for large multi-projects.
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:configuration_on_demand
# 按需配置(Configuration ondemand)只对任务相关的项目进行配置,这在大型多项目编译过程中非常有用,能够大幅度的减少不必要的配置时间。
org.gradle.configureondemand=true
另外,还有比较常见的用法,即利用该文件来配置网络代理,如:
systemProp.http.proxyHost=www.somehost.org
systemProp.http.proxyPort=8080
systemProp.http.proxyUser=userid
systemProp.http.proxyPassword=password
systemProp.http.nonProxyHosts=*.nonproxyrepos.com|localhost
Maven
前言
我们知道Gradle是在Ant与Maven的基础上优化而来的,而Maven在Ant的基础上进行改进的。对于Ant,他主要是处理打包构建的流程,其用Task进行工作的设计被Gradle继承与完善,但是他不支持依赖管理这一功能,这也是使用Eclipse(采用Ant)时,需要使用第三方包总是需要我们自己手动添加jar包。当然,这章我们主要讲Maven,因为他更接近Gradle,并且我们在上传jar或aar包(至Maven仓库)时,需要采用Maven插件,这就要求我们需要去了解相关的知识。
核心概念
POM (Project Object Model)
一个项目所有的配置都放置在 POM 文件中:定义项目的类型、名字,管理依赖关系,定制插件的行为等等(我们可以理解为,POM文件对于Maven就相当于.build文件对于Gradle中。Pom中的配置语言是xml,build中的是Groovy)。一下看一个简单示例:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.helloworld</groupId>
<artifactId>helloworld</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>helloworld</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Maven的习惯是通过 groupID(一般是组织的域名倒写,遵循Java package的命名习惯)+ artifactId(库本身的名称) + version(版本)来定义坐标, packaging用来表示包的类型,如jar或aar等。有了 maven 坐标,我们就可以用它来指定我们的项目所依赖的其他项目,插件,或者父项目。一般 maven 坐标写成如下的格式:
groupId:artifactId:packaging:version
像我们的例子就会写成:
com.mycompany.helloworld: helloworld: jar: 1.0-SNAPSHOT
Maven 依赖管理
前面我们说过,maven 坐标能够确定一个项目。换句话说,我们可以用它来解决依赖关系。在 POM 中,依赖关系是在 dependencies 部分中定义的。在上面的 POM 例子中,我们用 dependencies 定义了对于 junit 的依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
这个例子很简单,但是实际开发中我们会有复杂得多的依赖关系,因为被依赖的 jar 文件会有自己的依赖关系。那么我们是不是需要把那些间接依赖的 jar 文件也都定义在POM中呢?答案是不需要,因为 maven 提供了传递依赖的特性。
所谓传递依赖是指 maven 会检查被依赖的 jar 文件,把它的依赖关系纳入最终解决的依赖关系链中。依赖关系会存在一份POM文件中,在添加(下载)jar包的时候同时会下载POM文件,这样 maven 就能检查 junit 的依赖关系,把它所需要的依赖也包括进来。
在 POM 的 dependencies 部分中,scope 决定了依赖关系的适用范围。我们的例子中 junit 的 scope 是 test,那么它只会在执行 compiler:testCompile and surefire:test 目标的时候才会被加到 classpath 中,在执行 compiler:compile 目标时是拿不到 junit 的。scope 的默认值是 compile,即任何时候都会被包含在 classpath 中,在打包的时候也会被包括进去。
Gradle和Maven在依赖管理上几乎差不多,核心的概念是一样的,只不过Gradle语法更精简,如上面的依赖等价于:testCompile ‘junit:junit:4.12’,并且多了一些更灵活的自定义配置。
Maven仓库
在Maven中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。Maven在某个统一的位置存储所有项目的共享的构件,这个统一的位置,我们就称之为仓库,简单说:仓库就是存放依赖和插件的地方。(Gradle沿用了Maven仓库的使用)
Maven仓库分为两类:本地仓库和远程仓库(在远程仓库中又分成了3种:中央仓库 、 私服 、 其它公共库)
- 本地仓库
本地仓库,顾名思义,就是Maven在本地存储构件的地方。maven的本地仓库,在安装maven后并不会创建,它是在第一次执行maven命令的时候才被创建。maven本地仓库的默认位置:无论是Windows还是Linux,在用户的目录下都有一个.m2/repository/的仓库目录,这就是Maven仓库的默认位置。
远程仓库
这个章节讲Maven仓库,主要是为了介绍Maven远程仓库。中央仓库
说到远程仓库就先从最核心的中央仓库开始,中央仓库是默认的远程仓库,maven在安装的时候,自带的就是中央仓库的配置。中央仓库包含了绝大多数流行的开源Java(Android)构件,以及源码、作者信息、SCM、信息、许可证信息等。一般来说,简单的Java(Android)项目依赖的构件都可以在这里下载到。在Android Studio中默认采用的中央(Maven)仓库为jcenter,之前默认为mavenCentral(似乎这个名字更直观),简单理解jcenter在效能、使用、安全上都优于mavenCentral这个就行。
私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用。当Maven需要下载构件的时候,它从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载请求提供服务。我们还可以把一些无法从外部仓库下载到的构件上传到私服上。
Maven私服的 个特性:- 节省自己的外网带宽:减少重复请求造成的外网带宽消耗
- 加速Maven构件:如果项目配置了很多外部远程仓库的时候,构建速度就会大大降低
- 部署第三方构件:有些构件无法从外部仓库获得的时候,我们可以把这些构件部署到内部仓库(私服)中,供内部maven项目使用
- 提高稳定性,增强控制:Internet不稳定的时候,maven构建也会变的不稳定,一些私服软件还提供了其他的功能
- 降低中央仓库的负荷:maven中央仓库被请求的数量是巨大的,配置私服也可以大大降低中央仓库的压力
当前主流的maven私服:
- Apache的Archiva
- JFrog的Artifactory
- Sonatype的Nexus
上传构件至Maven仓库
以上讲完Maven的相关特性后,我们来看看其在Gradle的应用。如,上传构件至Artifactory仓库(对于Artifactory在服务器中配置相关我们这里就不介绍了):
添加相关插件
我们知道,在Gradle中,特性功能都是由插件来实现的,因此这个也必然需要我们添加相应的插件。
- 在最上层的(project)build.gradle文件添加一个关于Artifactory Gradle 插件的依赖:
buildscript {
dependencies {
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:3.1.1"
}
}
- 接下来,我们可以新建一个.build文件,并添加两个插件:一个准备Maven artifact maven-publish,另一个上传archives到artifactory com.jfrog.artifactory:
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'maven-publish'
定义关键的参数
//服务端地址
def PUBLICAT_REPO_URL='your_url'
//仓库账号密码
def artifactory_username='username'
def artifactory_password='password'
//定位依赖
//Maven的习惯是通过 groupID(一般是组织的域名倒写,遵循Java package的命名习惯)+ artifactId(库本身的名称) + version(版本)来定义坐标
def ARTIFACT_ID = 'com-example-groupname'
def GROUP_ID = 'com.example.groupname'
def VERSION_NAME = '1.0.0'
配置需要上传的文件
现在我们需要利用maven-publish配置我们需要上传的内容
publishing {
publications {
//aar构件配置
aar(MavenPublication) {
groupId GROUP_ID
version = VERSION_NAME
artifactId ARTIFACT_ID
// Tell maven to prepare the generated "* .aar" file for publishing
// 指定我们需要上传的内容:outputs里的*-release.aar文件
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
}
//往pom文件中写入相关依赖(依赖传递)
pom.withXml {
def dependencies = asNode().appendNode('dependencies')
configurations.getByName("_releaseCompile").getResolvedConfiguration().getFirstLevelModuleDependencies().each {
def dependency = dependencies.appendNode('dependency')
dependency.appendNode('groupId', it.moduleGroup)
dependency.appendNode('artifactId', it.moduleName)
dependency.appendNode('version', it.moduleVersion)
}
}
}
}
指定发布的位置
我们需要利用com.jfrog.artifactory 插件来指定artifact发布到的库。
artifactory {
//仓库地址
contextUrl = PUBLICAT_REPO_URL
publish {
repository {
//上传至指定的仓库(可以理解为指定目录)
repoKey = 'libs-release-local'
//仓库账号、密码
username = artifactory_username
password = artifactory_password
}
defaults {
// Tell the Artifactory Plugin which artifacts should be published to Artifactory.
publications('aar')
publishArtifacts = true
// Properties to be attached to the published artifacts.
properties = ['qa.level': 'basic', 'dev.team': 'core']
// Publish generated POM files to Artifactory (true by default)
publishPom = true
}
}
}
执行
gradle generatePomFileForAarPublication artifactoryPublish -p *