Gradle插件
插件工程结构
Gradle插件的工程结构如下图所示:
这个工程既可以作为单独的工程,也可以作为子工程存在,工程目录分为groovy和resources两个目录,分别存放插件groovy文件和配置文件。
build.gradle
还需要添加以下配置保证插件在不同版本的java平台正常运行
插件代码
声明Groovy文件,implements Plugin<Project>,在void apply(Project project) 方法中实现插件代码。在resources目录下META-INF.gradle-plugins创建一个properties文件,将Groovy文件的包名加类名声明在properties文件中,例如com.husor.android.makechannel.properties文件中:
implementation-class=com.husor.android.makechannel.MakeChannelPlugin
这就代表插件名为com.husor.android.makechannel,具体实现内容在MakeChannelPlugin文件中。
在apply方法中传入了project对象,表示我可以使用project对象的任意api,project对象可以获取到任意的工程中的文件、任务,那么能做的事情就很多了,比如复制文件、上传文件,android插件的打包过程就是通过android_sdk/tools下的工具,对当前工程中的各种文件进行编译、签名、对齐、打包等操作的。希望下一篇文章,可以学习一下安卓的打包过程。
插件发布
上传到本地testRepo目录下的插件包地址为com.beibei.android:makechannel:1.0.0-SNAPSHOT
上传到maven服务的插件包地址为com.beibei.android:makechannel:1.0.0
注意上传到本地的artifactId取决于project的name,而当前name为makechannel,上传到maven库的artifactId取决于声明
插件使用
Groovy
在学习Gradle之前,首先要了解Groovy,Groovy是一种脚本语言,基于Java又拓展了java的特性,欢迎参考官方文档 http://www.groovy-lang.org/documentation.html ,深入学习起来还是有一大片天地的,接下来我简单讲讲我理解的一些基本特性。
首先看一段代码:
task testGroovy << {
def var1 = 1024
println var1
var1 = "林帅"
println var1
println "my name is $var1"
println test1();
def var2 = 5
println var2.class
var2 = 5.5
println var2.class
var2 = 5L
println var2.class
var2 = 5D
println var2.class
var2 = 'hehe'
println var2.class
var2 = false
println var2.class
def demoList = [121, 3.14, 'hello', false, null] // 使用[]定义,元素之间用,隔开
println demoList.size // 获取集合大小
println demoList[2] // 获取index为2的元素
// 在结尾添加元素的两种写法
demoList.add(100)
demoList << 100
// 在指定位置添加元素,原本index大于等于3的元素往后退一位
demoList.add(3, 100)
demoList.remove(0) // 删除指定index的元素
demoList -= [3.14, false] // 删除某集合的元素
// demoList.clear() // 清空集合
// 使用集合直接调用.each可以对集合进行遍历
demoList.each {
println it // it是迭代过程中的每一个元素
}
def demoMap = ['name': '林帅', 'age': 18, 'isGay': false]
println demoMap.size() // 获取map大小
println demoMap.name // 通过key获取值
demoMap << ['hehe': '777'] // 添加元素
demoMap['haha'] = '888' // 添加元素
// 遍历map
demoMap.each {
println it.key
println it.value
}
pick(10, { a, b ->
if (a % 2 == 0)
println b * a
})
println testMethod(4, 5)
}
def String test1() {
return "xiaolinzi"
}
def pick(n, closure) {
for (i in 1..n) {
closure(i, 2)
}
}
//可以不写返回类型,不写参数类型,return可以不写,默认取最后一行
def testMethod(a, b) {
println a + b
3333
}
1024
林帅
my name is 林帅
xiaolinzi
class java.lang.Integer
class java.math.BigDecimal
class java.lang.Long
class java.lang.Double
class java.lang.String
class java.lang.Boolean
// list
5
hello
hello
100
null
100
100
// map
3
林帅
name
林帅
age
18
isGay
false
hehe
777
haha
888
// end
4
8
12
16
20
9
3333
1、弱类型,不用定义类型,类型之间也可以互相赋值,使用def标识
2、有分号或者没有都可以
3、方法有默认返回最后一行的数据,当最后一行没有时返回空,方法可以缺省返回值类型,默认public
4、在字符串中使用美元符访问变量,单引号和双引号的含义相同,三引号代表多行字符串
5、使用groovyc进行编译
6、Groovy的闭包是一个代码块或者方法指针,代码在某处被定义然后在其后的调用处执行
Groovy语法是在java预发基础上的升级版,有些特性是对java语法的补充提升,多用一下就可以理解了。
Gradle
首先搬官方文档 https://docs.gradle.org/current/userguide/userguide.html ,这个文档写的非常清楚,真的应该好好的读一遍,我只读了前几章,如果以后有机会一定再深入的学习一番。
Gradle的实质是配置脚本,执行一种类型的配置脚本时就会创建一个关联的对象,譬如执行Build script脚本就会创建一个Project对象,这个对象其实就是Gradle的代理对象。
Gradle的三种主要对象解释如下:
- Project对象:每个build.gradle会转换成一个Project对象。
- Gradle对象:构建初始化时创建,整个构建执行过程中只有这么一个对象,一般很少去修改这个默认配置脚本。
- Settings对象:每个settings.gradle会转换成一个Settings对象。
整个构建过程如图所示:
settings.gradle
放在工程的根目录,在多工程构建中用于标识对应的多工程module,可以动态的修改module的projectDir等信息。在工程构建的初始阶段为当前项目创建一个Settings类型的实例,创建项目层级结构的Project对象实例。
build.gradle
gradle构建的第二步是执行.gradle目录下的Init script脚本,为接下来的Build script做一些准备工作,一般我们不会修改这个脚本。
接下来第三步就是执行各个工程目录下的build.gradle脚本,将各个task任务及关系进行确认,达成最终的构建目标生成。
gradle构建最重要的就是理解task,其实插件本身就是一个task合集,插件是为了更好的复用和迁移代码,其实和写在build.gradle中是没有任何区别的。
常用的gradle command-line
1、name缩写 gradle aR => gradle assembleRelease
2、gradle -q projects
gradle -q tasks
gradle -q dependencies
前面都可以加上module名
3、-D system property
-P project property
-q Log error only
-i 查看log信息
-d 查看debug信息
https://docs.gradle.org/current/userguide/gradle_command_line.html
task
最后整理一下任务相关,可能整理不完善,后续不断加进来。
1、任务定义
task hello {
doLast {
println 'Hello world!'
}
}
task hello << { // 快捷定义
println 'Hello world!'
}
2、任务依赖
task intro(dependsOn: hello) {
doLast {
println "I'm Gradle"
}
}
3、动态添加任务目标
hello.doLast {
println "This is append hello"
}
4、properties声明
ext.pro1 = [ // extra properties
"test1": "properyTest1",
"test2": "properyTest2"
]
ext {
pro2 = "property2"
}
task testProperty { // 任务声明
ext.myProperty = "myValue"
doLast {
println hello.myProperty
println pro1.test1
println pro1.test2
println pro2
println project.properties.get("test")
println System.getProperty("test")
}
}
使用 gradle testProperty -Ptest=test1 -Dtest=test2 试试看吧
5、default task定义
defaultTasks 'hello'
默认执行hello task
6、理解gradle.taskGraph
gradle.taskGraph.whenReady { taskGraph ->
println taskGraph.allTasks
}
taskGraph对象包含当前目标任务所包含所有的任务对象
7、project api
project |
Project |
The Project instance |
---|---|---|
name |
String |
The name of the project directory. |
path |
String |
The absolute path of the project. |
description |
String |
A description for the project. |
projectDir |
File |
The directory containing the build script. |
buildDir |
File |
*projectDir*/build |
group |
Object |
unspecified |
version |
Object |
unspecified |
8、类型任务定义
task('hello') {
doLast {
println "hello"
}
}
task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
tasks.create(name: 'hello') {
doLast {
println "hello"
}
}
tasks.create(name: 'copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
9、File处理
fileCollection 当前路径下的File Set
fileTree 得到一个所有文件的列表
task testFiles {
project.files(project.buildDir.listFiles()).each {
println it.name
}
println("**********************")
FileTree tree = fileTree(project.buildDir)
tree.include 'outputs/aar/*.aar'
tree.exclude '*/*/*release*' // 这里必须是路径
tree.each {
println it.name
}
}
10、依赖管理
https://docs.gradle.org/current/userguide/dependency_management.html
中央仓库:
- mavenCentral,表示依赖是从Central Maven 2 仓库中获取的。
- jcenter,表示依赖是从Bintary’s JCenter Maven 仓库中获取的。
- mavenLocal,表示依赖是从本地的Maven仓库中获取的。
- 自定义maven仓库
- flatDir 表明系统将在lib目录下搜索依赖
repositories {
maven {
url "http://maven.petrikainulainen.net/repo"
url uri('/Users/xiaolinzi/testRepo') // 本地仓库
}
mavenCentral()
flatDir {
dirs 'libA', 'libB'
}
}
依赖分类:
- 当项目的源代码被编译时,compile配置项中的依赖是必须的。
- runtime 配置项中包含的依赖在运行时是必须的。
- testCompile 配置项中包含的依赖在编译项目的测试代码时是必须的。
- testRuntime 配置项中包含的依赖在运行测试代码时是必须的。
- archives 配置项中包含项目生成的文件(如Jar文件)。
- default 配置项中包含运行时必须的依赖。
- 在android还会用到provided表示在运行时依赖,编译时取消依赖
声明依赖的方式
dependencies {
compile group: 'org.springframework', name: 'spring-core', version: '2.5'
compile 'org.springframework:spring-core:2.5'
compile('org.hibernate:hibernate:3.0.5') {
transitive = true // 代表传递依赖被打开,默认打开
}
compile module("org.codehaus.groovy:groovy:2.4.7") { // 直接声明传递依赖的版本
dependency("commons-cli:commons-cli:1.0") {
transitive = false
}
module(group: 'org.apache.ant', name: 'ant', version: '1.9.6') {
dependencies "org.apache.ant:ant-launcher:1.9.6@jar",
"org.apache.ant:ant-junit:1.9.6"
}
}
compile project(':shared') // 依赖一个本地project
compile fileTree(dir: 'libs', include: '*.jar') // 依赖文件
compile gradleApi() //
compile localGroovy()
}
依赖冲突
去除依赖
configurations {
compile.exclude module: 'commons'
all*.exclude group: 'org.gradle.test.excludes', module: 'reports'
}
dependencies {
compile("org.gradle.test.excludes:api:1.0") {
exclude module: 'shared'
}
}
文档上还有很多东西没看透彻,以后慢慢补充吧,先写成这样。。>_<