这篇文章写的比较早,很多内容理解的不是很好,建议阅读本人最新文章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/