前言
Gradle框架只是定义了通用的构建流程,并没有实现具体构建任务,Gradle好比一个抽象类,Java构建,c++构建,Android构建则是一个个子类实现,交给开发者自行定义。Gradle插件就是开发者自定义构建逻辑的入口。
当掌握了Gradle基础之后,Gradle插件的定义并不难,难的是如何在实际情况中使用,设计自己的任务。
我了解到的Gradle插件基本上都是基于AGP插件的Transform api 结合字节码插装使用,本文主要介绍插件的定义
Gradle Plugin 概述
要编写Gradle插件的需要实现Plugin接口,将插件应用于项目时,Gradle会创建插件的实例,并调用插件的Plugin.apply()方法。 把项目对象当作参数传递。
Plugin接口是泛型类,可以设置插件的类型为:Gradle,Setting,Project(Gradle三个核心对象)。
分别对应三种配置文件
Gradle—init.gradle
Setting—settings.gradle
Project—build.gradle
如果Plugin 在settings.gradle 被引入 编译时会报错的,因为类型错误。
Gradle Plugin像是一种对当前 gradle脚本的代理。 插件中编写的代码,配置文件中都可以定义。区别在于插件提供了一种脚本逻辑的封装方式,把逻辑编写在插件里复用性更高。
定义插件有三种方式, 三种定义方式的区别在于,插件的作用域。
- 在脚本中定义插件
buildSrc
项目- 独立项目
当前有个多模块工程,具有模块A,B,C。 在模块A的build.gradle 文件中 编写插件 或 引入单独的 xxx.gradle文件。那么 插件逻辑只会在模块A生效,B,C模块不受影响。
如果定义buildSrc
项目,那么模块A,B,C都会受插件逻辑的影响。
如果为插件创建独立项目,生成并发布一个jar包,那么就在任意项目中使用。
Plugin :hello world
演示在脚本中创建插件 并 创建任务 输入 hello world
------------------demo.gradle-------------
//创建插件
class ProjectPlugin implements Plugin<Project>{
@Override
void apply(Project project) {
project.task("hello"){
println "配置阶段 任务名称 输出: hello"
doLast {
println "执行阶段 hello world"
}
}
println "配置阶段 输出:${project.name}"
}
}
//应用插件
apply plugin: ProjectPlugin
--------------------任意项目下 build.gradle----------------------
apply from: "../demo.gradle"
--------------------命令行执行 查看输出----------------------
gradle hello
定义个插件还是非常简单的,细节知识还是挺多的。
Gradle构建生命周期分三步:初始化,配置,执行。
所谓配置阶段 就是将项目内所有脚本文件执行一遍,创建任务。
执行阶段 实际是执行在配置阶段创建的任务。 每个Project
中持有 TaskContainer
。
用Java线程池概念类比,TaskContainer
就是一个线程池。TaskContainer
中保存任务。线程池中保存Runnable
配置阶段 约等于 向线程池中添加 Runnable
。 添加任务后 在一个恰当的时机 执行提前创建的任务,就是Gradle的执行阶段。
Extension扩展(插件传参)
接下来将会看到 熟悉的 android配置原理
android {
compileSdk 32
defaultConfig {
minSdk 23
targetSdk 32
versionCode 1
versionName "1.0"
}
}
------------------demo.gradle-------------
interface ProjectExtension {
Property<String> getName()
Property<Integer> getCode()
}
class ProjectPlugin implements Plugin<Project>{
@Override
void apply(Project project) {
def user = project.extensions.create("proExt",ProjectExtension.class)
project.task("hello"){
println "配置阶段 任务名称 输出: hello"
doLast {
println "执行任务 hello world"
println "执行任务 输出扩展信息 name:${user.name.get()} age:${user.code.get()}"
}
}
println "配置阶段 输出:${project.name}"
}
}
apply plugin: ProjectPlugin
--------------------任意项目下 build.gradle----------------------
apply from: "../demo.gradle"
proExt {
name = "小明"
code = 123
}
--------------------命令行执行 查看输出----------------------
gradle hello
定义接口,声明返回类型为 Property
的抽象方法, 定义普通JavaBean也可以达到同样的效果
需要强调的是,这两个小demo,都是在操作 void apply(Project project) {}
apply方法传入的Project参数。
在 build.gradle
中同样可以调用 extensions.create()
和 task()
方法。
Project 代表插件依赖的项目 。Project 是个接口, 每个build.gradle都会生成一个Project对象,代表当前项目。
涉及到对于Gradle脚本的理解问题 build.gradle
约等于 Project的实现类。子类自然可以随意调用Project暴露的方法
运行下方代码,创建任务hello2,可以看到在插件中创建任务同样的效果。
注意!!! 插件也好 脚本也罢 所有的操作 本质上都是操作 Project对象
--------------------任意项目下 build.gradle----------------------
task("hello2"){
doLast {
println "我是 ${project.name} 的任务 hello2"
}
}
--------------------命令行执行 查看输出----------------------
gradle hello2
buildSrc项目
创建一个Java项目,项目名必须是 buildSrc
,不需要在settings.gradle中配置include导入,创建项目后要注释掉,不然会提示重复导入。
buildSrc
是gradle的一个保留名称,Gradle会自动识别并导入项目,把它当作插件项目运行,编译在其他子项目之前。
项目加载完毕之后,需要声明插件,
- 创建
resources
目录 - 创建
META-INF
目录 - 创建
gradle-plugins
目录 - 创建
xxx.properties
配置文件,xxx会被外部识别为插件名字,比如:我们使用Android插件名称是com.android.application
,在AGP源码中配置文件被声明为com.android.application.properties
- 在配置文件中声明插件实现类
implementation-class=com.example.plugin.DemoPlugin
这样一个插件就配置好了,
buildSrc项目,可以用groovy,java,kotlin实现都可,demo使用Java
具体代码如下,与脚本实现插件并没有什么区别
public class DemoPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getExtensions().create("demoUser", UserExtension.class);
project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
UserExtension extension = project.getExtensions().getByType(UserExtension.class);
System.out.println(extension.toString());
}
});
}
}
public class UserExtension {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserExtension{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
-----------任意 build.gradle-----------
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.example.demo'
}
demoUser{
name = "xixixi"
age = 12
}
总结与参考
总体上说,单纯的写个可运行的Gradle插件还是比较简单没多少东西,难的是如何把插件机制和业务需求结合起来,emm 一直混小厂没见过啥相关案例,就不多说了。发布插件 上传maven没实践过也不多说了 有需要的同志自己研究一哈吧
开发自定义 Gradle 插件 官方文档浏览器机翻一下还是能看的