自定义 Gradle 插件

GitHub仓库代码

自定义 Gradle 插件三种方式:

  1. build script
  2. buildSrc
  3. 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

操作步骤:

  1. 在工程的根目录下新建 buildSrc 文件夹,rebuild工程 buildSrc 下会生成 .gradle + build 文件。

buildSrc 名字是规定的,不能出错。XXX、buildXXX等其他文件夹rebuild不能生成文件。

  1. 在 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'
//        }
//    }
//}
  1. 依次创建文件夹 buildSrc/src/main/groovy
  2. 创建包名 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
}
  1. 在项目或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'
}
  1. 验证
▶ ./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创建步骤:

  1. 在Android Studio的rootProject目录下新建一个Module,类型随便选一个就行(如 Android Module),后面会有大的改动。(也可以选择IDEA来开发,IDEA中可以直接创建groovy组件)
  2. 清空Module目录下build.gradle中的所有内容,删除其他所有文件
  3. 在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

  1. 新建AndroidModule,如命名为 CustomPluginModule;清空build.gradle内容,删除其他所有文件
  2. 修改 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
        }
    }
}
  1. 新建 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')
    }
}
  1. 创建 resources/META-INF/gradle-plugins 目录,并创建以 Plugin ID 命名的文件 com.example.plugin.properties (与使用相关 apply plugin: 'com.example.plugin' ) 写入如下内容:
// 插件的入口对应的类文件全路径 CustomPlugin.groovy
implementation-class=com.example.plugin.CustomPlugin
  1. 发布该插件到本地项目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
  1. 配置插件,在项目的根目录添加classpath和maven对应的本地uri
buildscript {
    repositories {
        google()
        jcenter()
        // 第五步发布的repo路径
        maven {
            url = uri("$rootDir/repo")
        }
    }
    dependencies {
        ...
        classpath "com.example.plugin:custompluginmodule:1.0.1-SNAPSHOT"
    }
}

allprojects {
    ...
}
  1. 引用插件
//方式三  Standalone project
apply plugin: 'com.example.plugin'
ext.greetingFile="$buildDir/hello.txt"
  1. 验证,执行插件任务(CustomPlugin中编写的writeToFile)
▶ ./gradlew writeToFile

> Task :app:writeToFile
hello world

方式3.1:Standalone project 独立 - 发布本地maven

  1. 安装Nexus服务
    Mac可直接使用brew安装
brew install nexus

Windows下载安装 https://www.sonatype.com/nexus/repository-oss-download

  1. 运行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 停止服务

  1. 打开Nexus本地仓库地址 http://localhost:8081/ ,点击设置图标
Nexus Repository Mananger
  1. Nexus创建仓库,如plugin-releases
  2. 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'
            }
        }
    }
}
  1. 执行上传任务
▶ ./gradlew uploadArchives
  1. 项目更路径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

  1. 引入插件
//方式三、四  Standalone project - 本地repo
// 通过 plugin id 引入插件
apply plugin: 'com.example.plugin'
ext.greetingFile="$buildDir/hello.txt"
  1. 验证
▶ ./gradlew writeToFile   

> Task :writeToFile
hello world

方式3.2:Standalone project 独立 - 发布中央仓库

  1. 注册sonatype账号:【申请上传资格】
    https://issues.sonatype.org/secure/Signup!default.jspa

  2. 登录
    https://issues.sonatype.org/secure/Dashboard.jspa

  3. 新建Issue
    https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&pid=10134

需要审核,Status状态从OPEN变成RESOLVED表示成功!如果中间有问题按Comment提示修改即可

  1. 查看Maven仓库
    https://s01.oss.sonatype.org/#view-repositories
    https://s01.oss.sonatype.org/

  2. 上传

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
  1. 使用
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中央仓库

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容