前言
在上一篇文章 Gradle插件-基础篇 中,我们学习了 Plugin 的设计规范,并且通过一个非常简单的例子对自定义 Plugin 有了初步认识,在这篇文章中,我们来继续学习 Gradle Plugin 更为深入的知识点。
本文参考 Gradle用户手册
Gradle插件
本文涉及的所有源码都位于 github
简单扩展
当我们引入 Android Plugin 时,在 android{}
有一些常用的配置,比如:
1 | android { |
如果我们想在自定义的 Plugin 中也使用这样的配置,可以使用 Gradle 提供的扩展功能:Extensions
,在第一篇文章中我们自定义了一个简单的 com.example.hello
插件,我们现在就在这个基础上添加一个扩展功能,最终实现的配置如下:
1 | hello { |
向 outFile 指定的文件中写入一句 “Hello World”,如文件不存在则创建它。
为了创建扩展,我们先定义一个数据模型用于读取配置:
1 | public class HelloModel { |
接着使用 ExtensionContainer
创建一个扩展:
1 | project.getExtensions().create("hello", HelloModel.class); |
getExtensions(String,Class,Object...)
这个方法有三个参数,第一个参数为扩展的名称即在 gradle 文件中使用的配置块,第二个参数为扩展的模型,用于定义可配置的属性,在上面的例子上,只有一个简单的 outFile
表示输出文件,第三个参数为可变长参数,表示模型的构造参数,在这个例子中,我们使用默认的无参构造函数。
经过上面的步骤,我们已经创建好了扩展,接下来是读取扩展的属性值,还记得我们之前说的,Gradle 是边读取边解释,所以当我们 apply plugin
时,会创建好扩展,而扩展的属性值配置只能在 apply plugin
之后,所以我们在 plugin 中直接读取扩展属性值是不行的,必须在项目评估完成之后:
1 | project.afterEvaluate(project1 -> { |
逻辑比较简单,我们现在直接运行在 .gradlew
即可,可以看到在 app 目录下生成一个 hello 文件:
1 | Hello World |
嵌套扩展
如果要想实现如下 DSL 配置:
1 | android { |
Gradle 也支持这种嵌套扩展,同样是上面的例子,我们现在想添加一个 info
配置块,将这部分的信息也写入到文件中,最终配置如下:
1 | hello { |
首先我们先创建一个 InfoModel,这里有个要注意的地方:用于实现扩展的类不能是 final
,因为最终用于扩展的实现类就是继承于这个类。
1 | static class InfoModel { |
上面在构造方法中添加 @Inject
注解是为了能用 Gradle 提供的 ObjectFactor
用于实现依赖注入功能。而在原来的 HelloModel
中,我们增加了 private final InfoModel info;
同时在构建函数中增加了 ObjectFactory
参数:
1 | public HelloModel(ObjectFactory objectFactory) { |
但这样还不够,还需要当 info
配置块被调用时所传递的实例:
1 | public void info(Action<InfoModel> action) { |
注意:这里的方法名和 DSL 中的使用的配置块名称一致。
最后我们在写入文件时,将这部分配置的信息也写入,同时在生成扩展时提供 ObjectFactor
实例:
1 | project.getExtensions().create("hello", HelloModel.class, project.getObjects()); |
最后执行下就可以看到结果了。
可以看到 Gradle 可以很方便地使用依赖注入。
配置容器
在 Android 项目中,我们可以去配置不同的构建变体:
1 | flavorDimensions "api", "mode" |
如果我们想要实现类似 productFlavors
这种动态生成的 DSL 配置块,我们可以使用 NamedDomainObjectContainer
来实现,接着上面的例子,我们最终的配置如下:
1 | hello { |
我们先创建 ValueModel
用于表示容器中每一项的格式,注意:这个类必须有个字段为 name
,比如上面的例子中,name
就是我们配置的 time
:
1 | public class ValueModel { |
这个类不能写成内部类,静态内部类也不行
接着,我们在 HelloModel
中同样创建 other
配置块,需要注意的是,这里我们要使用 NamedDomainObjectContainer
:
1 | public void other(Action<NamedDomainObjectContainer<ValueModel>> action) { |
而 other
则是这样子创建的:
1 | other = project.container(ValueModel.class); |
最后,同样的,我们将这块的配置也写入到 hello
文件中:
1 | StringBuilder otherString = new StringBuilder(); |
懒加载属性
在上面的例子中,我们都是通过使用原始数据类型去映射在构建脚本中的配置,这种方式会在配置完成之后直接映射,Gradle 4.0 提供了 Provider
和 Property
来提供懒加载属性,其中 Provider
表示不可变属性,Property
表示可变属性。这个 API 提供了两个好处:
- 可以放心地连接不同的数据模型,而不用担心某个数据模型的值是否已经知道。举个例子,比如你想将扩展中的属性映射到 task 中的属性,但扩展的属性值只有在构建脚本配置后才能知道。
- 避免在构建阶段进行资源密集型工作,比如当属性值时从文件中解析时
使用也比较简单,首先我们先声明一个Property<String>
然后使用 ObjectFactor
去创建:
1 | private final Property<String> testProperty; |
Gradle 会自动为
Property
生成 setter 方法,同时允许你使用 “=” 操作符来设置属性值
总结
在这篇文章中,我们主要讲的是,在自定义 Plugin 中经常用的到扩展功能,下一篇文章中,我们就开始进入 Plugin 的编写。