基于Gradle 8.2,创建Gradle插件

1. 前言

本文介绍在Android中,如何基于Gradle 8.2,创建Gradle插件。

1.1 本文环境

Android Studio 版本 : Android Studio Hedgehog | 2023.1.1
Gralde版本 : gradle 8.2
使用 Android Gradle 插件升级助理
Android Gradle 插件版本说明

1.2 为什么要写插件

可以将相关代码抽取出来,而不是耦合在build.gradle中,成为通用性的插件,可以在多个项目中复用。
比如com.android.application就是我们最为熟悉的Android插件。

plugins {
    id 'com.android.application'
}

2. 创建插件

2.1 新建项目

新建MyGradlePluginTest项目,这里选择Groovy DSL。

android gradle与gradle插件版本_android

2.2 新建Gradle插件Module

这里选择Java or Kotlin Library,也可以选择Android Library

android gradle与gradle插件版本_android_02

2.3 修改build.gradle
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        //MyTestPlugin { //这种方式也行
        create("MyTestPlugin") {
            //插件id
            id = 'com.heiko.myplugin'
            //插件的包名+类名
            implementationClass = 'com.heiko.myplugin.MyPlugin'
        }
    }
}
2.4 新建插件类

对应implementationClass中的路径,新建插件类 MyPlugin

package com.heiko.myplugin;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

class MyPlugin implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        System.out.println("这是我的第一个插件!");
    }
}

3. 发布插件

3.1 依赖maven-publish

在build.gradle中,添加maven-publish插件,然后配置发布插件的信息和仓库信息。
这里的maven仓库配置的是本地的,仅用作演示。实际项目中,应该使用真实的maven仓库服务器更合理些。
关于maven仓库更多的操作,详见我的另一篇文章 : Android Module上传到Maven仓库 及 实现同时上传到多个Maven仓库

plugins {
 	//...省略了代码...
    id 'maven-publish'
}

//...省略了代码...

println("原本的version:" + version)
println("原本的group:" + group)

//设置group
group = 'com.heiko.group'
//设置版本号
version = "1.0.0"

println("设置后的group:" + group)
println("设置后的version:" + version)

afterEvaluate {
    /*
    这部分代码不需要配置,会根据gradlePlugin中的进行生成
    除非使用老版本的插件依赖方式(classpath):需要指定artifactId的情况下,才需要使用
    publications {
        maven(MavenPublication) {
            groupId = group
            artifactId = 'MyPlugin'
            version = version

            from components.java
        }
    }*/
    publishing {
        // 配置仓库地址
        repositories {
            maven {
                url = uri('../repo')
            }
        }
    }
}

build.gradle的完整代码如下

plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
}

gradlePlugin {
    plugins {
        //MyTestPlugin { //这种方式也行
        create("MyTestPlugin") {
            //插件id
            id = 'com.heiko.plugin.myplugin'
            //插件的包名+类名
            implementationClass = 'com.heiko.myplugin.MyPlugin'
        }
    }
}

println("原本的version:" + version)
println("原本的group:" + group)

//设置group
group = 'com.heiko.group'
//设置版本号
version = "1.0.0"

println("设置后的group:" + group)
println("设置后的version:" + version)

afterEvaluate {
    /*
    这部分代码不需要配置,会根据gradlePlugin中的进行生成
    除非使用老版本的插件依赖方式(classpath):需要指定artifactId的情况下,才需要使用
    publications {
        maven(MavenPublication) {
            groupId = group
            artifactId = 'MyPlugin'
            version = version

            from components.java
        }
    }*/
    publishing {
        // 配置仓库地址
        repositories {
            maven {
                url = uri('../repo')
            }
        }
    }
}
3.2 发布插件

这个时候,点击下Sync同步下代码,然后可以在右侧Gradle的Tab中,找到publishing,里面有publish选项,我们双击它,就开始执行发布插件的操作了。

android gradle与gradle插件版本_Android_03


上传插件成功后,可以看到如下信息

android gradle与gradle插件版本_java_04

4. 使用插件

4.1 添加Maven仓库
pluginManagement {
    repositories {
    	//省略了其他仓库...
        maven {
            url 'repo'
        }
    }
}
4.2 依赖插件

在更目录下的build.gradle中,依赖插件

plugins {
    id 'com.android.application' version '8.2.0' apply false
    //省略了其他插件代码
    id 'com.heiko.plugin.myplugin' version '1.0.0' apply false
}

通常默认配置会 立马 解析(resolve) 并 应用(apply) 插件。
而 apply 设为 false, 表示 不应用插件到 根项目。目的是 提前解析 插件,保持一样的版本。

4.3 应用插件

在app目录下的build.gradle中,应用插件

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    //应用插件
    id 'com.heiko.plugin.myplugin'
}
4.4 同步项目

点击Sync同步下项目,可以看到我们的插件打印的日志了

android gradle与gradle插件版本_android_05

5. 使用Groovy编写插件

上文中我们是使用java语言来编写的插件,那么如果使用Groovy语言来编写插件,需要怎么操作呢 ?

5.1 添加groovy插件

为了识别groovy语言,我们需要在MyPlugin目录下的build.gradle中添加groovy插件

plugins {
    id 'java-gradle-plugin'
    //添加groovy插件
    id 'groovy'
    id 'maven-publish'
}
5.2 新建groovy目录

在和java同级别的目录下,新建groovy目录,groovy目录下需要和原java目录一样

android gradle与gradle插件版本_Android_06

5.3 新建MyPluginGroovy.groovy

在groovy目录下,要把MyPlugin.java改为MyPlugin.groovy ,这里我为了方便区分,把名称改为了MyPluginGroovy.groovy 。

package com.heiko.myplugin;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

class MyPluginGroovy implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        println "这是我的第一个Groovy插件!"
    }
}
5.4 修改implementationClass

修改MyPlugin目录下的build.gradle文件中的implementationClass ,修改为groovy目录下对应的插件类的包名+类名,注意没有文件后缀。

implementationClass = 'com.heiko.myplugin.MyPluginGroovy'

完整的build.gradle文件

plugins {
    id 'java-gradle-plugin'
    id 'groovy'
    id 'maven-publish'
}

gradlePlugin {
    plugins {
        create("MyTestPlugin") {
            //插件id
            id = 'com.heiko.plugin.myplugin'
            //插件的包名+类名
            implementationClass = 'com.heiko.myplugin.MyPluginGroovy'
        }
    }
}

//设置group
group = 'com.heiko.group'
//设置版本号
version = "1.0.1"

afterEvaluate {
    publishing {
        // 配置仓库地址
        repositories {
            maven {
                url = uri('../repo')
            }
        }
    }
}
5.5 重新发布插件

将版本号version改为1.0.1,双击publish,重新发布插件

5.6 依赖插件

修改项目根目录下的build.gradle,将我们的com.heiko.plugin.myplugin插件版本号修改为1.0.1。
重新运行项目,可以看到打印的插件日志变成了

这是我的第一个Groovy插件!
 

6. 使用Kotlin编写插件

除了使用Java或Groovy编写插件,我们还可以使用Kotlin来编写插件,使用kotlin对于Android开发者来说会更友好。

6.1 添加java插件

为了识别kotlin语言,我们需要在MyPlugin目录下的build.gradle中添加kotlin插件

plugins {
    id 'java-gradle-plugin'
    //添加Kotlin插件
    id "org.jetbrains.kotlin.jvm"
    id 'maven-publish'
}
6.2 新建kotlin目录

在和java同级别的目录下,新建kotlin目录,kotlin目录下需要和原java目录一样

android gradle与gradle插件版本_Gradle_07

6.3 新建MyPluginKotlin.kt

在kotlin目录下,要把MyPlugin.java改为MyPlugin.kt ,这里我为了方便区分,把名称改为了MyPluginKotlin.groovy 。

package com.heiko.myplugin

import org.gradle.api.Plugin
import org.gradle.api.Project


class MyPluginKotlin : Plugin<Project> {

    override fun apply(project: Project) {
        println("这是我的第一个Kotlin插件!")
    }
}
6.4 修改implementationClass

修改MyPlugin目录下的build.gradle文件中的implementationClass ,修改为groovy目录下对应的插件类的包名+类名,注意没有文件后缀。

implementationClass = 'com.heiko.myplugin.MyPluginKotlin'

完整的build.gradle文件

plugins {
    id 'java-gradle-plugin'
    id "org.jetbrains.kotlin.jvm"
    id 'maven-publish'
}

gradlePlugin {
    plugins {
        create("MyTestPlugin") {
            //插件id
            id = 'com.heiko.plugin.myplugin'
            //插件的包名+类名
            implementationClass = 'com.heiko.myplugin.MyPluginKotlin'
        }
    }
}

//设置group
group = 'com.heiko.group'
//设置版本号
version = "1.0.2"

afterEvaluate {
    publishing {
        // 配置仓库地址
        repositories {
            maven {
                url = uri('../repo')
            }
        }
    }
}
6.5 重新发布插件

将版本号version改为1.0.2,双击publish,重新发布插件

6.6 依赖插件

修改项目根目录下的build.gradle,将我们的com.heiko.plugin.myplugin插件版本号修改为1.0.2。
重新运行项目,可以看到打印的插件日志变成了

这是我的第一个Kotlin插件!

7. 补充 : Gradle 插件中的一些知识

7.1 Gradle Plugin中,Property和String的区别

在Gradle Plugin中,Property<String>和String之间的主要区别在于它们的动态性和可配置性。

  • String:
  • String是Java中的一个基本数据类型,用于表示文本或字符序列。
  • 在Gradle脚本或插件中,当你直接使用一个String时,它通常是一个静态的、不可变的值。
  • 一旦为String变量赋值,除非你重新为其赋值,否则它的值不会改变。
  • Property:
  • Property<String>是Gradle提供的一个更高级的类型,用于表示一个可配置的属性,其值类型为String。
  • 它是Gradle的模型(Model)系统的一部分,允许你在构建配置过程中动态地设置和查询属性的值。
  • Property<String>提供了更多的灵活性,因为它允许你在构建的不同阶段改变属性的值,甚至可以基于其他任务的输出或用户的输入来设置值。
  • 使用Property<String>而不是直接的String可以使你的构建脚本或插件更加灵活和可配置。
7.1.1 示例

假设你正在开发一个Gradle插件,该插件需要一个字符串属性来配置某个任务的行为。

如果你使用String:

public class MyPluginExtension {
    private String myProperty;
    
    public String getMyProperty() {
        return myProperty;
    }
    
    public void setMyProperty(String myProperty) {
        this.myProperty = myProperty;
    }
}

在这个例子中,myProperty是一个静态的String,一旦设置,除非显式地重新设置,否则它的值不会改变。

但是,如果你使用Property<String>:

import org.gradle.api.model.Property;
import org.gradle.api.provider.Provider;

public class MyPluginExtension {
    private final Property<String> myProperty;
    
    public MyPluginExtension(ObjectFactory objectFactory) {
        this.myProperty = objectFactory.property(String.class);
    }
    
    public Property<String> getMyProperty() {
        return myProperty;
    }
    
    // 你还可以提供其他方法来设置或查询属性的值,例如使用set方法或提供一个配置器(configurer)
}

在这个例子中,myProperty是一个Property<String>,它允许你在构建的不同阶段动态地设置和查询其值。你可以基于其他任务的输出、环境变量或用户的输入来设置这个属性的值。

总的来说,选择使用Property<String>还是String取决于你的具体需求和你希望为你的构建脚本或插件提供的灵活性和可配置性程度。

7.2 Gradle中,Property 对象能够响应值的变化,并且与Gradle的构建生命周期、任务依赖关系、缓存策略紧密集成。那么Property有哪些方法 ?

Gradle 中的 Property 对象是 Gradle 类型安全构建模型的核心组成部分,它代表了一个可以存储和操作值的容器。一个典型的 Property 对象可能提供以下方法(部分):

设置和获取值:

  • T get():获取当前存储在 Property 中的值。
  • void set(T value) 或者 void setValue(T value):设置 Property 的新值。
  • 可观察性:
  • addListener(PropertyChangeListener listener):添加一个监听器,当 Property 值发生改变时会触发通知。
  • removeListener(PropertyChangeListener listener):移除已添加的监听器。

验证和转换:

  • void convention(Action<? super T> action):定义一个“约定”动作,在属性未显式赋值时执行。
  • void finalizeValueOnRead():确保在读取属性值时进行最终计算或验证。
void validate(Closure clousre) 或 void validate(ValidationAction action):为 Property 添加验证逻辑。
  • 懒加载支持:
  • Provider asProvider():返回一个 Provider 对象,它可以延迟计算 Property 的值直到真正需要时。

参考文档

  1. Android Gradle 插件 API 更新:Android Gradle 插件 API 更新  |  Android Studio  |  Android Developers
  2. Android Gradle 插件版本说明:Android Gradle 插件 8.4 版本说明  |  Android Studio  |  Android Developers
  3. 官方Demo:gradle-recipes/Kotlin/modifyProjectClasses/app/build.gradle.kts at agp-7.4 · android/gradle-recipes · GitHub