概述
前面的博客Gradle 使用指南 – 创建Plugin 介绍了如何去创建一个插件,那么这篇文章将介绍一些深入的知识:如何对自定义插件进行 DSL 扩展。
在博客Gradle 使用指南 – Android DSL 扩展 Android 插件对 Gradle 进行的 DSL 扩展,那么我们自定义插件也是完全可以做到的。
Gradle 官方文档:Writing Custom Plugins
DSL 扩展基本实现
ExtensionContainer DSL 实现扩展
我们在进行 Gradle 配置时,很多的配置都是在 build.gradle 文件中进行的,插件可以在构建过程中获取这些输入。我们自定义的插件也是可以做到这一点的。这就要借助 ExtensionContainer
来实现。
怎么来得到一个 ExtensionContainer
对象呢?我们来看一下 Project
的 getExtensions()
方法:
1 | ExtensionContainer getExtensions(); |
它的返回者是一个 ExtensionContainer
对象。
通过 ExtensionContainer
我们可以向目标对象添加DSL扩展,通过 ExtensionContainer
的 create()
方法来创建新的 DSL 域,并与一个对应的委托类关联起来(即新建一个 DSL 域,并委托给一个具体类),通过它来跟踪传递给插件的所有设置和属性。
首先实现一个扩展类:
1 | class MyExtension { |
然后在插件中的 apply
方法中通过 project.extensions.create()
创建DSL扩展:
1 | public class MyFirstPlugin implements Plugin<Project>{ |
在 build.gradle 中通过引入插件后就可以配置了
1 | apply plugin: com.android.hq.myfirstplugin.MyFirstPlugin |
运行 myPlugin task 后可以通过打印看到配置中的输入。
传递参数
我们先来看一下 create
方法:
1 | <T> T create(String var1, Class<T> var2, Object... var3); |
第一个参数是在 build.gradle 中可以配置的代码块的方法名称;
第二个参数是关联的扩展实体类的名称
后面的参数表示传递给实体类构造函数的参数
比如想把 apply
方法的 project
参数传递进来,就需要这样引用:
1 | project.extensions.create('myExtension', MyExtension.class, project) |
那么对应 MyExtension
类要加一个带 Project
类的构造函数:
1 | class MyExtension { |
ExtensionContainer 实现子 Script blocks 配置
在使用配置 gradle 中我们一定使用过这样的配置:
1 | android { |
在 android
代码块中还可以进行 defaultConfig
代码块的配置,那么在自定义 Plugin 中这个配置方法也是可以实现的,也是要借助 ExtensionContainer
的 create()
方法来实现新的 DSL 域的创建。
比如在上面的例子中我们想在 myExtension
域中创建一个 defaultConfig
配置方法,那么可以在 MyExtension
中通过下面的代码来实现:
实现一个 DefaultConfig
类:1
2
3
4
5
6
7
8public class DefaultConfig {
String applicationId
int minSdkVersion
int targetSdkVersion
public DefaultConfig(String name) {
println "name = " + name
}
}
在 MyExtension
中添加映射关系:
1 | class MyExtension { |
1 | public class MyFirstPlugin implements Plugin<Project>{ |
配置代码:
1 | myExtension { |
ObjectFactory 实现子 Script blocks 配置扩展
从 Gradle 4.2 开始,提供了 ObjectFactory 类,使用它也可以实现嵌套 DSL 扩展。
参考 Gradle 官方文档:4.2 Writing Custom Plugins
插件代码:
1 | public class TestPlugin implements Plugin<Project>{ |
1 | public class MyExtension { |
1 | public class DefaultConfig { |
容器扩展
在进行 Android 配置时,我们一定用过 buildTypes
的配置,类似:
1 | buildTypes { |
这种类型可以用于在代码块中创建新的指定类型的对象。
先来看一下源码:1
2
3
4public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
this.checkWritability();
action.execute(this.buildTypes);
}
它传入的是一个 BuildType
类型列表的闭包代码。NamedDomainObjectContainer
是一个容器,追根溯源它是继承自 Collection<T>
。
我们这里叫它命名对象容器,可以用于在 builds cript 中创建对象,创建的对象必须要有 name 属性作为容器内元素的标识。
怎么来得到这样的容器对象呢?我们来看一下 Project
的 container
方法:
1 | <T> NamedDomainObjectContainer<T> container(Class<T> var1); |
下面通过实例来介绍一下。
实例介绍
首先创建一个 BuildConfig
类,上面说了,这个类必须有个 name
属性:
1 | public class BuildConfig { |
然后创建一个容器并将容器添加为 extension:
1 | // 注意这里要为构造函数增加 project 参数,看上面的参数传递一节 |
配置:
1 | myExtension { |
注意上面的扩展属性赋值,因为我们实现了 debugMode(boolean isDebug)
方法,所以 debugMode 加不加 =
号都是可以的,但是 applicationId 赋值时必须要用 =
号,否则会报错:1
Could not find method applicationId() for arguments [com.android.hq.test] on object of type com.android.hq.testplugin.TestPlugin$BuildConfig.