自定义 Gradle 插件三种方式:
- build script
- buildSrc
- Standalone project 独立 - 本地repo
3.1. Standalone project 独立 - 远程
方式1: build script
在构建脚本 build.gradle
中直接编写自定义插件的源代码。
好处:可以自动编译并包含在构建脚本的classpath中,不需要再去声明。
不足:这种方式实现的插件在构建脚本之外是不可见的,不能复用。
// 方式一 build script
// 项目根目录下 build.gradle 或者模块下 build.gradle 均可
class GreetingPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
//新建task hello
project.task('task_hello') {
doLast {
println 'Hello from the GreetingPlugin in app'
}
}
}
}
//引入插件
apply plugin: GreetingPlugin
验证:
▶ ./gradlew task_hello
> Task :task_hello
Hello from the GreetingPlugin in root
> Task :app:task_hello
Hello from the GreetingPlugin in app
方式2:buildSrc
该方式完整的目录结构
buildSrc
├──.gradle
├── build
├── build.gradle
└── src
└── main
└── groovy
└── com
└── buildsrc
└── GreetingExtensionPlugin.groovy
操作步骤:
- 在工程的根目录下新建
buildSrc
文件夹,rebuild工程buildSrc
下会生成.gradle + build
文件。
buildSrc 名字是规定的,不能出错。XXX、buildXXX等其他文件夹rebuild不能生成文件。
- 在 buildSrc 文件夹下新建 build.gradle 文件,内容如下
apply plugin: 'groovy'
sourceSets {
main{
groovy {
srcDir 'src/main/groovy'
}
resources {
srcDir 'src/main/resources'
}
}
}
//apply plugin: 'java-library'
//sourceSets {
// main{
// groovy {
// srcDir 'src/main/java'
// }
// resources {
// srcDir 'src/main/resources'
// }
// }
//}
- 依次创建文件夹 buildSrc/src/main/groovy
- 创建包名 com/buildsrc ,新建插件代码类
package com.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class GreetingExtensionPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// Add the 'greeting' extension object
def extension = project.extensions.create('greeting', GreetingExtension)
// Add a task that uses configuration from the extension object
project.tasks.create('task_buildSrc_greeting') {
doLast {
println "${extension.message} from ${extension.greeter}"
println project.greeting
}
}
}
}
class GreetingExtension {
String message
String greeter
}
- 在项目或Module的 build.gradle 引入插件
//build.gradle
//引入 1 不import会提示找不到GreetingExtensionPlugin
//import com.buildsrc.GreetingExtensionPlugin
//apply plugin: GreetingExtensionPlugin
// or 引入 2
apply plugin: com.buildsrc.GreetingExtensionPlugin
greeting {
message = 'hello'
greeter = 'GreetingExtensionPlugin'
}
- 验证
▶ ./gradlew task_buildSrc_greeting
> Task :app:task_buildSrc_greeting
hello from GreetingExtensionPlugin
extension 'greeting'
扩展属性:自定义插件代码中有一句def extension = project.extensions.create('greeting', GreetingExtension),使用这种方式来给插件代码传递参数。
如果修改代码重新编译报错或不生效,可删除build文件夹重试
存在GreetingExtensionPlugin/GreetingExtension飘红情况提示已经存在,不影响运行
方式3:Standalone project 独立 - 本地repo
上面两种自定义插件都只能在自己的项目中使用,如果想在其他项目中也能复用,可以创建一个单独的项目并把这个项目发布成一个JAR,这样多个项目中就可以引入并共享这个JAR。通常这个JAR包含一些插件,或者将几个相关的task捆绑到一个库中,或者是插件和task的组合, Standalone project创建步骤:
- 在Android Studio的rootProject目录下新建一个Module,类型随便选一个就行(如 Android Module),后面会有大的改动。(也可以选择IDEA来开发,IDEA中可以直接创建groovy组件)
- 清空Module目录下build.gradle中的所有内容,删除其他所有文件
- 在Module中创建src/main/groovy的目录,然后再创建包名文件夹。在main目录下再新建resources/META-INF/gradle-plugins目录,在这个目录下编写一个和插件id名字相同的.properties文件,这样Gradle就可以找到插件实现了。
示例插件功能:新建build目录,并新建个文件写入内容
完成项目目录如下:
├── build.gradle
└── src
└── main
├── groovy
│ └── com
│ └── example
│ └── plugin
│ └── CustomPlugin.groovy
└── resources
└── META-INF
└── gradle-plugins
└── com.example.plugin.properties
- 新建AndroidModule,如命名为
CustomPluginModule
;清空build.gradle
内容,删除其他所有文件 - 修改 build.gradle 内容如下:
plugins {
id 'groovy'
id 'maven-publish'
id 'maven'
}
sourceSets {
main{
groovy {
srcDir 'src/main/groovy'
}
resources {
srcDir 'src/main/resources'
}
}
}
dependencies {
implementation gradleApi()
implementation localGroovy()
}
repositories {
mavenCentral()
}
group = 'com.example.plugin'
version = '1.0.1-SNAPSHOT'
publishing {
repositories {
maven {
url = uri("$rootDir/repo")
}
}
publications {
maven(MavenPublication) {
from components.java
}
}
}
- 新建 src/main/groovy 目录,在其路径下创建包名目录如
com.example.plugin
,并创建一个名为 CustomPlugin.groovy` 的文件,内容如下:
package com.example.plugin
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.TaskAction
class CustomPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.tasks.create('writeToFile', CustomPluginTask) {
destination = { project.greetingFile }
doLast {
println project.file(destination).text
}
}
}
}
class CustomPluginTask extends DefaultTask {
def destination
File getDestination() {
//创建路径为destination的file
project.file(destination)
}
@TaskAction
def greet() {
def file = getDestination()
file.parentFile.mkdirs()
//向文件中写入文本
file.write('hello world')
}
}
- 创建
resources/META-INF/gradle-plugins
目录,并创建以 Plugin ID 命名的文件com.example.plugin.properties
(与使用相关apply plugin: 'com.example.plugin'
) 写入如下内容:
// 插件的入口对应的类文件全路径 CustomPlugin.groovy
implementation-class=com.example.plugin.CustomPlugin
- 发布该插件到本地项目repo,
build.gradle
中的 publishing 任务(注意命令为publish)
▶ ./gradlew publish
或在Android Studio的右上角打开Gradle,执行:plugin分组中的publish命令,执行完成后,会在项目根目录下生成repo仓库。
repo
└── com
└── example
└── plugin
└── custompluginmodule
├── 1.0.1-SNAPSHOT
│ ├── custompluginmodule-1.0.1-20210309.061711-1.jar
│ ├── custompluginmodule-1.0.1-20210309.061711-1.jar.md5
│ ├── custompluginmodule-1.0.1-20210309.061711-1.jar.sha1
│ ├── custompluginmodule-1.0.1-20210309.061711-1.jar.sha256
│ ├── custompluginmodule-1.0.1-20210309.061711-1.jar.sha512
│ ├── custompluginmodule-1.0.1-20210309.061711-1.module
│ ├── custompluginmodule-1.0.1-20210309.061711-1.module.md5
│ ├── custompluginmodule-1.0.1-20210309.061711-1.module.sha1
│ ├── custompluginmodule-1.0.1-20210309.061711-1.module.sha256
│ ├── custompluginmodule-1.0.1-20210309.061711-1.module.sha512
│ ├── custompluginmodule-1.0.1-20210309.061711-1.pom
│ ├── custompluginmodule-1.0.1-20210309.061711-1.pom.md5
│ ├── custompluginmodule-1.0.1-20210309.061711-1.pom.sha1
│ ├── custompluginmodule-1.0.1-20210309.061711-1.pom.sha256
│ ├── custompluginmodule-1.0.1-20210309.061711-1.pom.sha512
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ ├── maven-metadata.xml.sha1
│ ├── maven-metadata.xml.sha256
│ └── maven-metadata.xml.sha512
├── maven-metadata.xml
├── maven-metadata.xml.md5
├── maven-metadata.xml.sha1
├── maven-metadata.xml.sha256
└── maven-metadata.xml.sha512
- 配置插件,在项目的根目录添加classpath和maven对应的本地uri
buildscript {
repositories {
google()
jcenter()
// 第五步发布的repo路径
maven {
url = uri("$rootDir/repo")
}
}
dependencies {
...
classpath "com.example.plugin:custompluginmodule:1.0.1-SNAPSHOT"
}
}
allprojects {
...
}
- 引用插件
//方式三 Standalone project
apply plugin: 'com.example.plugin'
ext.greetingFile="$buildDir/hello.txt"
- 验证,执行插件任务(CustomPlugin中编写的writeToFile)
▶ ./gradlew writeToFile
> Task :app:writeToFile
hello world
方式3.1:Standalone project 独立 - 发布本地maven
- 安装Nexus服务
Mac可直接使用brew安装
brew install nexus
Windows下载安装 https://www.sonatype.com/nexus/repository-oss-download
- 运行Nexus服务
▶ nexus run
_ __
/ | / /__ _ ____ _______
/ |/ / _ \| |/_/ / / / ___/
/ /| / __/> </ /_/ (__ )
/_/ |_/\___/_/|_|\__,_/____/
Sonatype Nexus (3.30.0-01)
Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or 'system:shutdown' to shutdown.
...
-------------------------------------------------
Started Sonatype Nexus OSS 3.30.0-01
-------------------------------------------------
输入system:shutdown
停止服务
- 打开Nexus本地仓库地址
http://localhost:8081/
,点击设置图标
- Nexus创建仓库,如
plugin-releases
- build.gradle 增加上传任务
// 发布-nexus服务maven上传
apply plugin: 'maven'
uploadArchives{
repositories{
mavenDeployer{
//正式发布仓库
repository(url:"http://localhost:8081/repository/plugin-releases/"){
authentication(userName:"admin",password:"admin")
}
//快照版本的仓库
// snapshotRepository(url:"http://localhost:8081/repository/plugin-snapshots/"){
// authentication(userName:"admin",password:"admin")
// }
pom.project {
//版本号,如果是快照版本,其版本号后面应该添加-SNAPSHOT,否则不能正常识别上传
version '1.0.0'
//一般写项目名称即可
artifactId 'customplugin'
//组别,类似包名,保证唯一性
groupId 'com.example.pluginhappy'
//打包格式
packaging 'aar'
//描述
description 'plugin'
}
}
}
}
- 执行上传任务
▶ ./gradlew uploadArchives
- 项目更路径build.gradle 添加插件配置
buildscript {
repositories {
...
//方式四 Standalone project - maven仓库 - 配置
maven {
url 'http://localhost:8081/repository/plugin-releases/'
}
}
dependencies {
...
//方式四 Standalone project - maven仓库 - 配置
classpath "com.example.pluginhappy:customplugin:1.0.0"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
插件的依赖方式 classpatch,类似库的 compile
- 引入插件
//方式三、四 Standalone project - 本地repo
// 通过 plugin id 引入插件
apply plugin: 'com.example.plugin'
ext.greetingFile="$buildDir/hello.txt"
- 验证
▶ ./gradlew writeToFile
> Task :writeToFile
hello world
方式3.2:Standalone project 独立 - 发布中央仓库
注册sonatype账号:【申请上传资格】
https://issues.sonatype.org/secure/Signup!default.jspa新建Issue
https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&pid=10134
需要审核,Status状态从OPEN变成RESOLVED表示成功!如果中间有问题按Comment提示修改即可
apply plugin: 'maven'
uploadArchives{
repositories{
mavenDeployer{
//正式发布仓库
// repository(url:"https://s01.oss.sonatype.org/content/repositories/releases/"){
// authentication(userName:"admin",password:"admin")
// }
//快照版本的仓库
snapshotRepository(url:"https://s01.oss.sonatype.org/content/repositories/snapshots/"){
authentication(userName:"admin",password:"admin")
}
pom.project {
//版本号,如果是快照版本,其版本号后面应该添加-SNAPSHOT,否则不能正常识别上传
version '0.0.1-SNAPSHOT'
//一般写项目名称即可
artifactId 'customplugin'
//组别,类似包名,保证唯一性
groupId 'com.github.simplehych'
//打包格式
packaging 'aar'
//描述
description 'plugin'
}
}
}
}
执行上传命令
▶ ./gradlew uploadArchives
- 使用
buildscript {
repositories {
...
// 方式五 Standalone project - 中央maven仓库 - 配置
maven {
url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
}
}
dependencies {
...
//方式五 Standalone project - 中央maven仓库 - 配置
classpath "com.github.simplehych:customplugin:0.0.1-SNAPSHOT"
}
}
//方式三、四、五 Standalone project - 本地repo
// 通过 plugin id 引入插件
apply plugin: 'com.example.plugin'
ext.greetingFile = "$buildDir/hello.txt"
参考资料
感谢以下文章作者
Gradle官网
Gradle系列之初识Gradle
Gradle理论与实践四:自定义Gradle插件
Maven(6) Java上传本地jar包到maven中央仓库