这篇文章写的比较早,很多内容理解的不是很好,建议阅读本人最新文章Gradle开发快速入门——DSL语法原理与常用API介绍
环境
配置gradle(加入环境变量)
简单插件开发
新建一个gradle项目,在工程(主工程或子工程均可)根目录添加一个HelloPlugin.gradle文件
-
apply plugin: HelloPlugin -
class MyExtension { -
Boolean enable = true -
String text = '' -
} -
class HelloPlugin implements Plugin<Project> { -
@Override -
void apply(Project project) { -
project.extensions.create('hello', MyExtension) -
project.task('hello') << { -
MyExtension ext = project.extensions.hello; -
if (ext.enable) { -
println "Hello ${ext.text}!" -
} else { -
println 'HelloPlugin is disabled.' -
} -
} -
} -
}
在工程根目录对应的build.gradle中添加以下代码
-
// 调用HelloPlugin.gradle中的代码 -
apply from: 'HelloPlugin.gradle' -
// 设置参数 -
hello { -
enable = true -
text = 'World' -
}
在工程根目录执行命令行,即可看到插件中定义的Task(hello)被执行
-
➜ GradleStudy gradle hello -
:hello -
Hello World! -
BUILD SUCCESSFUL -
Total time: 0.675 secs
独立工程中开发插件
创建工程
后面的示例用命令行直接开发,先创建一个文件夹(project-dir)用于存放工程。用命令行写Java比较麻烦,但是之所以用命令行,是为了更好的理解gradle。
➜ ~ mkdir hello_proj➜ ~ cd hello_proj➜ hello_proj
也可以使用AndroidStudio或IDEA创建一个空的gradle项目。直接使用RootProject开发,则project-dir就是根目录;如果新建子模块开发,则project-dir就是这个模块的目录。
工程根目录下创建工程的gradle配置文件:<project-dir>/build.gradle
-
➜ hello_proj vim build.gradle -
apply plugin: 'groovy' -
apply plugin: 'maven' -
repositories { -
mavenCentral() -
} -
dependencies { -
// compile 'org.codehaus.groovy:groovy-all:2.3.11' // Groovy支持(远程) -
compile localGroovy() // Groovy支持(本地) -
compile gradleApi() // GradleAPI支持 -
}
根目录再创建一个settings.build文件配置工程(如果直接使用RootProject,也可以不创建这个文件):<project-dir>/settings.gradle
-
➜ hello_proj vim settings.gradle -
rootProject.name = 'HelloPlugin'
创建源码
源码放在groovy插件默认的SourceSet源码目录下:<project-dir>/src/main/groovy/<package>/<class>.groovy
也可以在build.gradle中通过SourceSet命令指定源码和资源所在目录。
-
➜ hello_proj mkdir -pv src/main/groovy/com/jzj/groovy/ -
➜ hello_proj cd src/main/groovy/com/jzj/groovy -
➜ groovy vim MyExtension.groovy -
➜ groovy vim HelloPlugin.groovy -
package com.jzj.groovy -
class MyExtension { -
Boolean enable = true -
String text = '' -
} -
package com.jzj.groovy -
import org.gradle.api.Plugin -
import org.gradle.api.Project -
class HelloPlugin implements Plugin<Project> { -
@Override -
void apply(Project project) { -
project.extensions.create('hello', MyExtension) -
project.task('hello') << { -
MyExtension ext = project.extensions.hello; -
if (ext.enable) { -
println "Hello ${ext.text}!" -
} else { -
println 'HelloPlugin is disabled.' -
} -
} -
} -
}
创建资源文件
资源文件放在groovy插件默认的SourceSet资源目录下:<module-dir>/src/main/resources/META-INF/gradle-plugins/<plugin-name>.properties
示例中定义的plugin-name是HelloPlugin
-
➜ groovy cd .. -
➜ jzj cd .. -
➜ com cd .. -
➜ groovy cd .. -
➜ main mkdir -pv resources/META-INF/gradle-plugins -
➜ main ls -
groovy resources -
➜ main cd resources/META-INF/gradle-plugins -
➜ gradle-plugins vim HelloPlugin.properties -
implementation-class=com.jzj.groovy.HelloPlugin
此时在命令行中查看文件结构如下。如果是IDEA或AndroidStudio,源码和资源文件的目录会被显示成对应的图标。
-
➜ hello_proj tree -
. -
├── build.gradle -
├── settings.gradle -
└── src -
└── main -
├── groovy -
│ └── com -
│ └── jzj -
│ └── groovy -
│ ├── HelloPlugin.groovy -
│ └── MyExtension.groovy -
└── resources -
└── META-INF -
└── gradle-plugins -
└── HelloPlugin.properties -
9 directories, 5 files
源码可在此下载
打包
在命令行所在目录执行gradle build打包
-
➜ hello_proj gradle build -
:compileJava UP-TO-DATE -
:compileGroovy -
:processResources -
:classes -
:jar -
:assemble -
:compileTestJava UP-TO-DATE -
:compileTestGroovy UP-TO-DATE -
:processTestResources UP-TO-DATE -
:testClasses UP-TO-DATE -
:test UP-TO-DATE -
:check UP-TO-DATE -
:build -
BUILD SUCCESSFUL -
Total time: 1.714 secs
打包后目录结构如下,默认文件输出到build目录下,build/libs/HelloPlugin.jar就是最终生成的插件
-
➜ hello_proj tree -
. -
├── build -
│ ├── classes -
│ │ └── main -
│ │ └── com -
│ │ └── jzj -
│ │ └── groovy -
│ │ ├── HelloPlugin$_apply_closure1.class -
│ │ ├── HelloPlugin.class -
│ │ └── MyExtension.class -
│ ├── libs -
│ │ └── HelloPlugin.jar -
│ ├── resources -
│ │ └── main -
│ │ └── META-INF -
│ │ └── gradle-plugins -
│ │ └── HelloPlugin.properties -
│ └── tmp -
│ ├── compileGroovy -
│ │ └── groovy-java-stubs -
│ └── jar -
│ └── MANIFEST.MF -
├── build.gradle -
├── settings.gradle -
└── src -
└── main -
├── groovy -
│ └── com -
│ └── jzj -
│ └── groovy -
│ ├── HelloPlugin.groovy -
│ └── MyExtension.groovy -
└── resources -
└── META-INF -
└── gradle-plugins -
└── HelloPlugin.properties -
24 directories, 11 files
插件使用
在需要使用插件的工程build.gradle中配置如下
BuildScript配置
通常在RootProject中的build.gradle中配置buildscript。和项目中的dependencies不同,buildscript代码块中的dependencies是在编译阶段需要依赖的包,而不会被编译进工程中。
在buildscript中添加对Gradle插件的依赖。
- 对于已经发布到远程的插件,可以使用
classpath 'group:name:version'的格式。 - 也可以将插件发布到本地Maven仓库,在repositories中添加本地maven仓库。
- 还可以直接用本地jar文件,使用
classpath files('xxx.jar')的方式依赖。
buildscript {repositories {jcenter()// maven { url uri('../repo') } // 指定本地maven仓库的路径// ...}dependencies {// classpath 'com.jzj.gradle:HelloPlugin:0.0.1' // 依赖远程插件classpath files('HelloPlugin.jar') // 依赖本地文件,需要将jar文件放到项目根目录}}
Project配置
对于需要使用插件的Project,在其build.gradle中添加下面的脚本。
-
// 应用插件 -
apply plugin: 'HelloPlugin' -
// 插件配置 -
hello { -
enable = true -
text = 'World' -
}
执行
在Project根目录运行gradle指令即可看到效果。
-
➜ GradleStudy gradle hello -
:hello -
Hello World! -
BUILD SUCCESSFUL -
Total time: 0.675 secs
执行原理浅析
Groovy
- Groovy是一种脚本语言,在Java基础上进行了一些扩展,支持闭包、动态类型等特性,兼容Java代码。
- 每个Groovy脚本文件会编译生成一个继承自
groovy.lang.Script的Java class。
动态类型
-
def var = 'text' -
println var -
var = 5 -
println var + 1 -
Object var = "text"; -
System.out.println((String)o); -
var = 5; -
System.out.println(String.valueof((Integer)o + 1));
闭包 Closure
-
Closure c = { a, b -> -
println a -
println b -
} -
c.call('text1', 5) -
public static void main(String[] args) { -
abstract class MyClosure { -
abstract void call(Object a, Object b); -
} -
MyClosure c = new MyClosure() { -
@Override -
void call(Object a, Object b) { -
System.out.println(a); -
System.out.println(b); -
} -
}; -
c.call("text1", 5); -
}
代理对象 DelegateObject
- 每个闭包都有一个代理对象,在闭包上未找到的属性和方法都会转给代理对象。
-
class MyDelegate { -
def func() { -
println 'func' -
} -
} -
Closure c = { -
func(); -
} -
c.delegate = new MyDelegate() -
c.call() -
public static void main(String[] args) { -
class MyDelegate { -
void func() { -
System.out.println("func"); -
} -
} -
abstract class MyClosure { -
Object delegate; -
boolean callMethod(Object o, String method, Object... args) { -
try { -
Method func = o.getClass().getDeclaredMethod(method); -
if (func != null) { -
func.invoke(o, args); -
return true; -
} -
} catch (Exception e) { -
// do nothing. -
} -
return false; -
} -
abstract void call(); -
} -
MyClosure c = new MyClosure() { -
@Override -
void call() { -
if (!callMethod(this, "func")) { -
callMethod(delegate, "func"); -
} -
} -
}; -
c.delegate = new MyDelegate(); -
c.call(); -
}
this, owner, delegate
-
class Cls { -
def mCls = this; -
def a, b, c; -
def static assertSameObj(x, y) { -
assert x.is(y); -
} -
def static assertSameObj(Closure x, y) { -
assert x.is(y); -
} -
def start() { -
println 'this = ' + this -
a = { -
println 'a.this = ' + this -
println 'a.owner = ' + owner -
println 'a.delegate = ' + delegate -
assertSameObj(this, mCls) -
assertSameObj(owner, mCls) -
assertSameObj(delegate, owner) -
b = { -
println 'b.this = ' + this -
println 'b.owner = ' + owner -
println 'b.delegate = ' + delegate -
assertSameObj(this, mCls) -
assertSameObj(owner, a) -
assertSameObj(delegate, owner) -
c = { -
println 'c.this = ' + this -
println 'c.owner = ' + owner -
println 'c.delegate = ' + delegate -
assertSameObj(this, mCls) -
assertSameObj(owner, b) -
assertSameObj(delegate, owner) -
} -
println('c = ' + c) -
c.call(); -
// 修改c的delegate,并调用c没有但delegate有的方法 -
c.delegate = new String('123'); -
println('length = ' + c.length()) -
} -
println('b = ' + b) -
b.call(); -
} -
println('a = ' + a) -
a.call() -
} -
} -
new Cls().start(); -
this = Cls@33990a0c -
a = Cls$_start_closure1@50b5ac82 -
a.this = Cls@33990a0c -
a.owner = Cls@33990a0c -
a.delegate = Cls@33990a0c -
b = Cls$_start_closure1$_closure2@6babf3bf -
b.this = Cls@33990a0c -
b.owner = Cls$_start_closure1@50b5ac82 -
b.delegate = Cls$_start_closure1@50b5ac82 -
c = Cls$_start_closure1$_closure2$_closure3@7ea9e1e2 -
c.this = Cls@33990a0c -
c.owner = Cls$_start_closure1$_closure2@6babf3bf -
c.delegate = Cls$_start_closure1$_closure2@6babf3bf -
length = 3
结论:
-
语法上直接将Closure赋值给Object编译器会有警告,但不影响实际运行。闭包最终也是通过Object实现的。
-
this指向其外部的Object对象,指定义闭包的类
-
owner指向其外部的Object/Closure,指直接包含闭包的类或闭包
-
delegate默认和owner一致,且可以修改,指用于解析闭包中属性和方法调用的第三方对象
Gradle
-
Gradle为基于Groovy的一种领域专用语言(DSL/Domain Specific Language)
-
每个Gradle脚本文件编译生成的类除了继承自
groovy.lang.Script,同时还实现了接口org.gradle.api.Script。 -
Gradle工程build时,会执行
settings.gradle、build.gradle脚本;settings脚本的代理对象是Setting对象,build脚本的代理对象是Project对象。
Gradle Delegate
Build脚本对应的Project对象从6个范围中查找方法:
- Project对象本身定义的方法
- 脚本文件中定义的方法
- 被插件添加的extension. extension的名字可以做为方法名
- 被插件添加的convension方法。
- 工程中的task。task的名字可以作为方法名
- 父工程中的方法。
例如在build.gradle中,常会使用dependencies语句块。
-
// build.gradle -
dependencies { -
compile 'xxx:xxx:1.0' -
testCompile 'xxx:xxx:1.0' -
}
-
dependencies是Project对象中定义的DSL方法,后面的大括号是其接受的闭包参数;这里Groovy的括号可以省略;
-
闭包的delegate是
DependencyHandler getDependencies(),因此其内部可以直接调用DependencyHandler定义的compile/testCompile等方法;
下面的写法也是可以的。
-
// build.gradle -
dependencies { -
compile 'xxx' -
} -
getProject().dependencies ({ -
compile('xxx') -
}) -
getProject().getDependencies().compile('xxx') -
project.dependencies.compile('xxx')
apply plugin
如果分析gradle的源码可以知道,执行apply plugin时,会执行Plugin的apply方法,apply中project.extensions.create('hello', MyExtension)动态给Project对象创建了名为hello的extensions,因此在apply之后,可以使用hello(Closure)。
如果把apply和MyExtention的位置调换,gradle编译时就会报错,提示找不到DSL。
Error:(4, 0) Gradle DSL method not found: 'hello()'Possible causes:<ul><li>The project 'Gradle' may be using a version of Gradle that does not contain the method.<a href="open.wrapper.file">Open Gradle wrapper file</a></li><li>The build file may be missing a Gradle plugin.<a href="apply.gradle.plugin">Apply Gradle plugin</a></li>
Internal Gradle Plugins
-
从Gradle源码可以看到其内部实现了
JavaPlugin、GroovyPlugin、WarPlugin(JavaWebApplication)等基础插件。 -
实际上dependencies中的compile就是由
JavaBasePlugin动态创建的一个Configuration DSL。 -
groovy、android等插件在apply时也会先apply JavaBasePlugin,所以就有了compile、sourceSet等DSL。
-
// org.gradle.api.plugins.JavaBasePlugin.java -
private void defineConfigurationsForSourceSet(SourceSet sourceSet, ConfigurationContainer configurations) { -
Configuration compileConfiguration = configurations.maybeCreate(sourceSet.getCompileConfigurationName()); -
compileConfiguration.setVisible(false); -
compileConfiguration.setDescription(String.format("Dependencies for %s.", sourceSet)); -
// ... -
sourceSet.setCompileClasspath(compileClasspathConfiguration); -
sourceSet.setRuntimeClasspath(sourceSet.getOutput().plus(runtimeConfiguration)); -
} -
// groovy plugin -
public void apply(ProjectInternal project) { -
project.getPluginManager().apply(JavaBasePlugin.class); -
// ... -
}
build流程
Gradle脚本的build流程分为3个阶段:
-
初始化阶段
执行Settings脚本。Gradle支持单个和多个工程的编译。在初始化阶段,Gradle判断需要参与编译的工程,为每个工程创建一个Project对象,并建立工程之间的层次关系。 -
配置阶段
执行Build脚本。Gradle对上一步创建的Project对象进行配置。 -
执行阶段
执行选中的task,例如build,assembleDebug等。
参考资料与扩展阅读
-
《Gradle脚本基础全攻略》 http://blog.csdn.net/yanbober/article/details/49314255
-
《Groovy脚本基础全攻略》 http://blog.csdn.net/yanbober/article/details/49047515
-
《Chapter 40. Writing Custom Plugins》 https://docs.gradle.org/current/userguide/custom_plugins.html
-
《GRADLE脚本的语法和BUILD流程》 http://www.jianshu.com/p/20f6695a9bd5
-
《深入理解Android(一):Gradle详解》 http://www.infoq.com/cn/articles/android-in-depth-gradle/