这篇文章讲给大家带来gradle打包系列中的高级用法-自己动手编写gradle插件。我们平常在做安卓开发时,都会在android这个插件提供的功能内使用,大部分情况下,配置好这个插件就够了,但是有时候我们想做一些额外的拓展,比如对build输出的Apk进行上传。当然通过在工程中添加额外的task就可以了,但是如果把这个功能做成插件,就会更加通用。那今天我们就从groovy基础语法开始,剖析一下gradle的原理,最后教大家如何自定义Gradle插件。
这篇文章里面会涉及到Groovy的语法,gradle的基础知识等等,我不会专门去讲,但是凡是在与本次主题相关的概念都会给大家梳理一下。
在Gradle构建中,我们需要在build.gradle文件或者settings.gradle里面写上自己的配置,这些配置语句,看起来是符合某个标准的,就像SQL语句那样,我们如果要查询就得用Select语句,然后按照Select支持的语法来书写。我们如果想对gradle的构建过程进行配置就得遵循Gradle支持的语法。这样在某个领域通用的语言就叫做DSL(Domain specific languange ), 直译就是领域专用语言。
Martin Fowler将DSL分为两类:外部DSL和内部DSL。外部DSL是一种独立的可解析的语言,举一个最常见的是例子,SQL,它专注于数据库的操作。内部DSL是通用语言所暴露的用来执行特定任务的API,它利用语言本身的特性,将API以特殊的形式(或者格式)暴露出来的,就像gradle,它是基于groovy的,groovy是是一种通用语言,但是gradle基于groovy的语法,构建了自己的一套DSL,我们在配置gradle时,必须首先遵循groovy的语法,其次还必须遵循Gradle 的DSL标准。
闲话少说,要自己动手编写Gradle插件,必须首先对Groovy的语法有一定的了解,尤其是Groovy中的闭包语法。
Groovy essentials
Groovy是一种基于JVM的语言,了解groovy的一些语法,对于理解并用活gradle有很大的帮助
Groovy语法特性一:方法的输入参数优化
groovy中定义的函数,如果至少有一个参数,在调用的时候可以省略括号。比如这样
def func(String a){
println(a)
}
func 'hello'
在gradle有大量省略括号调用函数的例子,比如
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
....
}
比如这里compileSdkVersion 和 buildToolsVersion 其实就是调用了同样名字的两个函数,在AndroidStudio里面可以点进去查看函数实现
当然如果某个函数没有参数,那就不能省略括号,否则会当成一个变量使用
Groovy语法特性二:闭包
闭包(Closure)是groovy中一个很重要的概念,而且在gradle中广泛使用。what is closure? 简而言之,闭包是一个可执行的代码块,类似于C语言中的函数指针。在很多动态类型语言中都有广泛的使用,java8 中也有类似的概念:lambda expression,但是groovy中的闭包和java8中的lambda表达式相比又有很多的不同之处。稍后会提到,先来学习一下groovy中的闭包
def listener = { e -> println "Clicked on $e.source" }
此时listener就是一个闭包,
闭包参数
闭包参数的构成有三个元素,
- 一个可选的参数类型
- 一个可选的参数默认值
- 参数名称
def xx = {
int a=2 ->
a + b
}
闭包对象
grooy中闭包是Closure类型的实例,比如上面的闭包我们又可以定义为:
Closure xx = {
int a=2 ->
a + b
}
而且你可以制定一个可选的返回类型
Closure xx = {
int a=2 ->
a + b
}
隐含参数
如果闭包内没有生命任何参数,没有->, 那么闭包内置会定义一个隐含参数it
def greeting = { "Hello, $it!" }
闭包代理(Closure delegate)
闭包区别lambda表达式的一个显著的区别在与groovy中的闭包可以指定代理。
实际上在闭包内,可以拿到三个对象:
- This 对应于定义闭包时包含他的class,可以通过getThisObject或者直接this获取
class EnclosedInInnerClass {
class Inner {
Closure cl = { this }
}
void run() {
def inner = new Inner()
assert inner.cl() == inner
}
}
- owner 对应于定义闭包时包含他的对象,可以通过getOwner或者直接owner获取
class EnclosedInInnerClass {
void run() {
def nestedClosures = {
def cl = { owner }
cl()
}
assert nestedClosures() == nestedClosures
}
}
-
delegate 闭包对象可以指定一个第三方对象作为其代理,用于函数调用或者属性的指定,可以通过getDelgate或者delegate属性获取
class Person { String name } class Thing { String name } def p = new Person(name: 'Norman') def t = new Thing(name: 'Teapot') def upperCasedName = { delegate.name.toUpperCase() } upperCasedName.delegate = p assert upperCasedName() == 'NORMAN' upperCasedName.delegate = t assert upperCasedName() == 'TEAPOT'
代理策略
如果在闭包内,没有明确指定属性或者方法的调用是发生在this, owner,delegate上时,就需要根据代理策略来判断到底该发生在谁身上。有如下几种代理策略:
Closure.OWNER_FIRST 默认的策略,如果属性或者方法在owner中存在,调用就发生在owner身上,否则发生在delegate上
Closure.DELEGATE_FIRST 跟owner_first正好相反
Closure.OWNER_ONLY 忽略delegate
Closure.DELEGATE_ONLY 忽略owner
Closure.TO_SELF 调用不发生在owner或delegate上,只发生在闭包内
class Person {
String name
def pretty = { "My name is $name" }
String toString() {
pretty()
}
}
class Thing {
String name
}
def p = new Person(name: 'Sarah')
def t = new Thing(name: 'Teapot')
#调用发生在owner
assert p.toString() == 'My name is Sarah'
p.pretty.delegate = t
assert p.toString() == 'My name is Sarah'
#调用发生在delegate
p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
assert p.toString() == 'My name is Teapot'
groovy中的闭包还有很多种高级的使用方法,但是在gradle中比较少用到,你可以点击这个链接 Groovy闭包 去了解。
Groovy语法特性三:类的Property
Groovy中的class和java中的Class区别不大,值得我们关注的区别是,如果类的成员变量没有加任何权限访问,则称为Property, 否则是Field,filed和Java中的成员变量相同,但是Property的话,它是一个private field和getter setter的集合,也就是说groovy会自动生成getter setter方法,因此在类外面的代码,都是会透明的调用getter和setter方法
class Person {
String name
void name(String name) {
this.name = "Wonder$name"
}
String wonder() {
this.name
}
}
def p = new Person()
p.name = 'Marge' //调用setter方法
assert p.name == 'Marge' //调用getter方法
p.name('Marge')
assert p.wonder() == 'WonderMarge'
我们在gradle脚本中经常会看到这样的属性
//gradle扩展
ext{
name 'haha'
}
这里name属性实际上就是调用了setName方法,并且利用了之前说的省略括号的特性,实际上就是 调用了setName('haha')
还有我们常见的在project中创建task的方法
task taskName << {
doSomeThing
}
实际上就是调用了task(taskName).leftShift(closure),这里又扯出来Groovy中的两个语法特性,一个是操作符重载,这个早C++里面用到很多,这个语法特性可以让你的类,可以像基本类型一样使用普通的操作符,比如 + - * / 等,只需要你重载了这些函数。
class Bucket {
int size
Bucket(int size) { this.size = size }
Bucket plus(Bucket other) {
return new Bucket(this.size + other.size)
}
}
def b1 = new Bucket(4)
def b2 = new Bucket(11)
assert (b1 + b2).size == 15 //b1 和b2 可以直接相加
上面那个task的例子,就是因为task类重载了leftShift,因此可以使用<< 操作符
//Project类
Task task(String var1) throws InvalidUserDataException;
//Task类
Task leftShift(Closure var1);
task例子中用到的另外一个语法特性是Command Chains, 这个特性不仅可以省略函数调用中的括号,而且可以省略,连续函数调用中的. 点号, 比如
a(b).c(d) 这里a c是函数, b,d是函数参数, 就可以缩写为a b c d。这个特性强大之处在于不仅适用于单个参数类型函数,而且适用于多个参数类型的函数,当参数类型为闭包时同样适用, 比如这样
task xxxxx doLast { println "task doLast 1"} doLast { println "task doLast 2"}
这么看起来是不是跟sql语句就有点儿像了,哈哈,DSL。
上面所提到的那些groovy的语法特性,构成了Gradle中DSL的基础,理解了这些就能理解groovy是如何为gradle dsl提供支撑的。
说完了groovy的这些基础语法,我们再来学习一下gradle的一些基础知识,这样就不难理解。
Gradle Basics
gradle的语法和入门指导,可以从gradle的官网上找到,如果需要像教科书那样的教程,完成可以去官网上学习,我们这里只会从中提炼出一些核心知识,了解了这些核心知识,我认为就足够了。
我们在AndroidStudio中创建基于Gradle的project时,会默认生成一个多项目结构的Gradle工程,像下面这样
对于这个project,我们能对构建过程做出改变的,就只能发生在这些.gradle文件中,这些文件称为Build Script构建脚本。对于Gradle中的构建脚本,你一方面可以理解为配置文件,每一种类型脚本文件都是对某一中类型的构建对象进行配置。但另一方面你可以把每个脚本理解为一个Groovy闭包,这样我们在执行构建脚本时,就是在执行每一个闭包函数,只不过每个闭包所设置的delegate不一样。
Build Script
Gradle中共有三种类型的构建脚本:build script, init script, setting script,分别对应三种类型的Gradle构建对象。
我们先来说说这三种不同的构建脚本:
Init Script
init script我们大部分人并不常用,但是它确实可以配置,你可以通过如下方式指定init script
- 通过在命令行里指定gradle参数 -I 或者--init-script
- 在USER_HOME/.gradle 或者 USER_HOME/.gradle/init.d目录下,放置init.gradle文件
- 将init.gradle放置在GRADLE_HOME/init.d/目录下
init.gradle脚本中,我们可以对当前的build做出一些全局配置,比如全局依赖,何处寻找插件等。
initscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.apache.commons', name: 'commons-math', version: '2.0'
}
}
Settings script
Settings.gradle文件在项目创建于rootProject根目录下,因为settings.gradle对于多项目工程来说是必须的,然而对于单项目project来说不是必须的。Settings脚本的Delegate是Settings对象,因此我们可以瞅瞅Settings类里面都有哪些函数
我们可以看到有常见的include 函数 和 includeFlat函数, 因为我们最常做的事情就是在settings脚本里面写上include ':app'这样的函数调用语句,根据groovy的语法,他就是在gradle生成的settings对象调用函数 include('app')
include接受的参数是一个string数组,因此include后可以加很多参数,这个函数的意义就是:指明那些子project参与这次gradle构建
Build Script
build.gradle脚本文件使我们最常见的,绝大部分配置工作发生在这里,因为它的Delegate就是Project,如果是多项目结构,则build.gradle文件分为两种,rootProject下的build.gradle文件,subProject下的build.gradle文件,每个build.gradle文件执行时的代理project都不一样。
project中有很多函数,比如我们最常见的apply, dependencies等
//apply一个插件或者脚本
void apply(Map options);
//配置当前project的依赖
void dependencies(Closure configureClosure);
//配置当前脚本的classpath
void buildscript(Closure configureClosure);
这些函数都可以在AndroidStudio里点击到Project里面去一探究竟,如果理解了groovy的闭包,相信不会再对build.gradle里面那些配置感到神秘,他们也只是在某个对象上调用了某个函数,传入了某个闭包或者其他对象作为参数
至于这里面提到的gradle, project, settings对象何时生成,我们可以通过了解gradle的build lifecycle了解到
Build life cycle
Gradle最终的目的是运行gradle中的task(task我们后面会单独介绍)
gradle
在gradle运行的过程中,gradle会经历三个阶段
Initialization 在这个阶段,gradle会首先生成gradle对象和settings对象,然后执行init.gradle中脚本,再执行settings.gradle中的脚本,根绝settings.gradle给每个项目生成一个project对象
Configuration 在这个阶段,gradle会运行参与本次构建的所有project中的build.gradle文件,这个阶段完成之后,每个project中的所有task以及相互关系就确定了
Execution 执行阶段,gradle会根据传给它的task名字运行指定的task
监听器
我们还可以在上述build lifecycle中添加额外的监听器,监听某件事情的完成,这样我们就可以对gradle的正常运行流程做干预,比如我们想监听某个subProject 配置完毕,打印日志。我们可以这样
gradle.afterProject { project ->
println('Project ' + project + ' has evaluated')
}
这段代码通常需要添加在rootProject的build.gradle脚本中,也可以添加到subProject中(只要能通过gradle获取到Gradle对象),但是这个监听器的安装需要发生在subProject在evaluated时,因此如果前面已经有project参与过evaluate,就不会得到监听。
还可以通过如下方法监听:
allprojects {
afterEvaluate { project ->
if (project.hasTests) {
println "Adding test task to $project"
project.task('test') << {
println "Running tests for $project"
}
}
}
}
监听task的创建
可以在build.gradle中通过如下方式监听task的创建
tasks.whenTaskAdded { task ->
task.ext.srcDir = 'src/main/java'
}
监听整个task关系图的创建
gradle.taskGraph.whenReady {
}
监听某个task的执行
//监听整个build完毕
gradle.buildFinished {
}
//监听某个task开始执行,结束执行
gradle.taskGraph.addTaskExecutionListener(new TaskExecutionListener() {
@Override
void beforeExecute(Task task) {
}
@Override
void afterExecute(Task task, TaskState state) {
}
})
任意类型监听器
/**
* Adds the given listener to this build. The listener may implement any of the given listener interfaces:
*
*
* - {@link org.gradle.BuildListener}
*
- {@link org.gradle.api.execution.TaskExecutionGraphListener}
*
- {@link org.gradle.api.ProjectEvaluationListener}
*
- {@link org.gradle.api.execution.TaskExecutionListener}
*
- {@link org.gradle.api.execution.TaskActionListener}
*
- {@link org.gradle.api.logging.StandardOutputListener}
*
- {@link org.gradle.api.tasks.testing.TestListener}
*
- {@link org.gradle.api.tasks.testing.TestOutputListener}
*
- {@link org.gradle.api.artifacts.DependencyResolutionListener}
*
*
* @param listener The listener to add. Does nothing if this listener has already been added.
*/
public void addListener(Object listener);
可以通过gradle.addListener(listener) 传入一个gradle支持的listener类型。上面addListener的注释中列出了它支持的监听器类别,你的listener可以任意实现其中的接口来满足你的需求。
创建Task
创建一个简单的task的语法为
task << {
}
将groovy语法的时候,我们提到过,这句话应该这么理解:
- 首先调用project的task方法,传入一个taskName,返回一个task
- 调用task的leftShift 方法 传入一个closure,根据leftShift的解释,我们知道这个闭包将添加到task的action list里去。在任务执行的时候运行
有时候,我们可能会错写成
task {
}
少了这个<< 操作符,意思就大不一样了,这个时候调用的函数为
//Project类
Task task(String name, Closure configureClosure);
这时,第二个参数closure用来配置task,在task创建的时候执行,而不是在task执行的时候运行。不过我们可以在这个闭包内配置task的一些属性。
//指定Copy类型task的属性
task copyDocs(type: Copy) {
from 'src/main/doc'
into 'build/target/doc'
}
当然我们还可以这样指定task的行为:
task exampleTask {
doLast{
}
}
task exampleTask doLast{
}
因为Task的doLast函数的作用和<<操作符一样。
task的类型
task name(type: Type){
doLast{
}
}
gradle内置为我们生成了很多task类型,比如Copy,Delete,可以点击链接 查看gradle内置的task类型列表,如果创建task时没有指定type,则他默认是DefaultTask类型。我们还可以创建自己的task类型,我们在稍后就会讲到。
我们还可以可以指定task之间的依赖关系, 通过dependsOn, mustRunAfter, shouldRunAfter来指定。 还可以指定task的分组group, 如果不指定,将会出现在other里面。
自定义Gradle插件
我们先来看看gradle默认会生成哪些task,我们从app中删除以下code:
apply plugin: 'com.android.application'
android{
...
}
dependencies{
....
}
再次运行gradlew app:tasks 会发现
只有9个task,而且全都出现在help分组中,很多task都不见了,很容易才想出, 这些task都是android application插件生成的。我们能使用Gradle构建Android 工程,一切都基于这个插件。这个插件从android这个扩展中读取了我们的配置,生成了一些列构建android 所需要的任务。
如果我们想生成自己的Task,执行额外的任务呢?
我们需要扩展,首先我们需要扩展任务的类型,其次我们需要编写我们自己的插件,这两者并不是紧密联系的,有时候我们编写插件的时候,也许并不需要扩展任务类型,有时候我们并不需要编写额外的插件,只需要新写一种任务类型就能达到我们的目的。但大部分时候,这两者我们都需要,我们还需要实现我们自己的Extension,有了Extension我们就能从build.gradle脚本中读取我们需要的属性。因此我们需要做的事情是:
接下我们看看怎么利用AndroidStudio编写我们自己的Gradle插件。AS默认不会为我们生成Groovy插件项目的结构,我们需要自己构建出来。首先新建一个Android Project, 在app subProject中src/main/下新建groovy和resources目录,删除其他目录,并修改build.gradle文件,像这样
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
在groovy木下新建HelloPlugin.groovy文件在resources目录下新建path:
META-INF/gradle-plugins/com.junli.HelloPlugin.properties,最终目录结构看起来就像这样:
这里的com.junli.HelloPlugin最终会出现在
apply plugin: 'com.junli.HelloPlugin'
我们开始编写HelloPlugin.groovy
package com.junli
import org.gradle.api.Plugin
import org.gradle.api.Project
class HelloPlugin implements Plugin
{
@Override
void apply(Project project) {
project.task('helloPluginTask') {
group 'junli'
doLast {
println 'hell i am a task in plugin'
}
}
}
}
我们定义了一个类HelloPlugin,让其继承自gradle api中的Plugin
为了插件能成功apply, 我们还需要在META-INF/gradle-plugins/com.junli.helloPlugin.properties中写入
implementation-class=com.junli.HelloPlugin
这句话指定了插件类的位置。
我们如何将其apply到自己的工程中去呢?,首先我们必须说明的是,插件可以以三种形式存在:
在我们构建项目的build.gradle脚本中直接编写
在我们构建项目的rootProjectDir/buildSrc/src/main/groovy 目录下
以单独的project存在
在这里我们采用的是第三种形式,这种形式的好处在于可以将插件打包发布,并很好的分享给使用者。
OK,那我们改如何分享呢?,我们可以先来学习如何在发布到本地,这样我们机器上的其他工程就能在指定目录下找到我们的插件。
要将插件发布,我们需要利用maven-publish插件
apply plugin: 'maven-publish'
publishing{
publications {
mavenJava(MavenPublication) {
from components.java
groupId 'com.junli'
artifactId 'helloPlugin'
version '0.1'
}
}
repositories{
maven {
// change to point to your repo, e.g. http://my.org/repo
url "../repo"
}
}
}
应用了插件maven-publish后,我们事先extension publishing,并在publications下添加我们需要发布的配置,这里我们添加了一个mavenJava,这个名字可以随意命名。其他配置还有
from 指定需要发布的组件,目前仅有两种java 和 web
groupId、artifactId、version,这三者组成了插件使用者在声明依赖时的完整语句 groupId:artifactId:version
默认打包时只会包含编译过的jar包,我们还可以将源代码和javadoc打包发布,可以通过artifact指定:
task javadocJar(type: Jar, dependsOn: groovydoc) {
classifier = 'javadoc'
from "${buildDir}/javadoc"
}
task sourcesJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}
mavenJava(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
groupId 'com.junli'
artifactId 'helloPlugin'
version '0.1'
}
这样我们运行gradlew app:tasks就可以看到创建了如下tasks:
有两种publish任务,publish 和 publishToMavenLocal,
publish任务依赖于所有的mavenPublication的generatePomFileFor任务和publishxxxPublicationToMavenRepository,意思是将所有的mavenPublication发布到指定的repository,
publishToMavenLocal依赖于所有的mavenPublication的generatePomFileFor和publishxxxTomavenLocal任务,意思是讲所有的mavenPublication发布到本地的m2 repository。
我们运行
gradlew app:publish
我们会在rootProjectDir的repo目录下看到我们生成的repo:
我们看到了目录结构为 groupId/artifactId/version, 每个版本下面有我们打包生成的jar包,每个jar包的校验文件(md5和sha1),以及pom文件。pom文件是一个xml结构的文件,用来描述maven项目:包括配置文件;开发者需要遵循的规则,缺陷管理系统,组织和licenses,项目的url,项目的依赖性,以及其他所有的项目相关因素。
应用插件
我们改如何应用插件呢?因为我们在插件已经publish到本地的目录中,因此我们可以在本地直接使用,我们在项目先新建一个子项目MyLibrary,为了使用插件我们需要修改build.gradle文件
apply plugin: 'com.junli.HelloPlugin' //应用插件
buildscript {
repositories {
maven {
url uri('../repo') //插件所在的目录
}
}
dependencies {
classpath 'com.junli:helloPlugin:0.1' //添加依赖
}
}
首先添加依赖,依赖寻找的repo需要添加我们之前发布的目录,然后应用插件.
我们运行graldew mylibrary:tasks可以看到mylibrary项目中生成了一个新的task:
Junli tasks
helloPluginTask
处与junli分组下
运行任务打印出
hell i am a task in plugin
通过这样一个简单的例子,我们了解了如何编写插件和应用插件,接下来我们通过添加Extension和Task来让我们的插件变得更强大
Extension
我们在HelloPlugin同级目录下新建文件PersonExt.groovy
public class PersonExt {
String name;
int age;
boolean boy;
@Override
public String toString() {
return "I am $name, $age years old, " + (boy?"I am a boy":"I am a gril");
}
}
并修改HelloPlugin的内容
class HelloPlugin implements Plugin
{
@Override
void apply(Project project) {
project.extensions.add("personExt", PersonExt)
project.task('printPerson') {
group 'junli'
doLast{
PersonExt ext = project.personExt
println ext
}
}
}
}
我们对project添加了一个extension,这样子,我们可以在使用插件的地方,实现这个extension,我们能够在插件apply的时候拿到这个extension, 读取extension中的属性,
personExt{
name 'Tom'
age 10
boy false
}
我们再次运行我们的任务
gradlew mylibrary:printPerson
I am Tom, 10 years old, I am a gril
你可以看到这里的name和age 都没有使用=号进行复制,就是利用了grooy特性,它实际上是调用了setName方法,并且省略了方法调用的括号。
如果我们想读取一个列表,这个列表的长度不定呢?
我们需要使用NamedDomainObjectContainer,我们后面都简称NDOC 这是一个容纳object的容器,它的特点是它的内部使用SortedSet实现的,内部对象的name是unique的,而且是按name进行排序的。通常创建NDOC的方法就是调用
[NamedDomainObjectContainer] container(Class type)
这里type有一个要求:必须有一个public的构造函数,接受string作为一个参数,必须有一个叫做name 的property。
接下来我们看如何获取一个person的列表
class HelloPlugin implements Plugin
{
@Override
void apply(Project project) {
//创建一个容器
NamedDomainObjectContainer persons = project.container(Person)
//将容器添加为extension
project.extensions.add('team', persons)
def task = project.task('showTeam'){
group 'junli'
doLast {
def team1 = project.extensions.getByName('team')
println team1
}
}
}
}
这样我们只需要修改extension为:
team{
John{
age = 10
boy = false
}
Tom{
age = 20
boy = false
}
}
最后通过println 我们可以看到我们这个容器中的内容:
[I am John, 10 years old, I am a gril, I am Tom, 20 years old, I am a gril]
我们了解了如何通过extension读取列表,接下来,我们可以混合列表和单个属性,就像android{...}一样
我们新建一个类Team
//Team.groovy
class Team {
NamedDomainObjectContainer persons;
String name;
int count;
public Team(NamedDomainObjectContainer persons){
this.persons = persons;
}
//persons函数,允许我们通过配置传入闭包,来给persons容器添加对象
def persons(Closure closure){
persons.configure(closure)
}
@Override
String toString() {
return "This is a team, name: $name, count: $count" + " persons: "+ persons
}
}
这里的persons(closure)函数是必须的,只有实现了这个函数,Gradle在解析team的extension,遇到persons配置时,才能通过调用函数,调用 NamedDomainObjectContainer的configure方法,往里面添加对象。
class HelloPlugin implements Plugin
{
@Override
void apply(Project project) {
NamedDomainObjectContainer persons = project.container(Person)
Team team = new Team(persons)
project.extensions.add('team', team)
def task = project.task('showTeam'){
group 'junli'
doLast {
def team1 = project.extensions.getByName('team')
println team1
}
}
}
}
team{
name = 'GSW'
count = 20
persons{
John{
age = 10
boy = false
}
Tom{
age = 20
boy = false
}
}
}
运行任务showTeam输出为:
This is a team, name: GSW, count: 20 persons: [I am John, 10 years old, I am a gril, I am Tom, 20 years old, I am a gril]
这样我们就通过一个简单的例子就熟悉了Gradle插件的编写规则,而且通过对groovy语法的了解,让我们对gradle的DSL不再陌生,好了这是第一篇我们要讲的内容,下一篇我会带来自定义Task,复杂的plugin,上传jcenter等内容,小伙伴记得关注哦。