前言
自定义Gradle插件是一个很常见的操作,Gradle插件能帮我们解决很多项目打包问题、CICD问题、效率提升问题。
这篇文章讲解如何自定义一个Gradle插件,如何将插件打包上传到本地文件仓库和maven仓库,如何使用插件。笔者将结合一个案例来讲解插件插件从定义、打包到使用的全流程。这个案例实现功能是将Application Module中的dependencies{…}配置部分交给插件,在云端来管理项目的依赖。
本文主要围绕几下几个话题展开:
- 如何自定义一个Gradle插件
- Gradle插件打包到本地文件仓库
- Gradle插件打包到自建maven仓库
- 如何使用自定义的Gradle插件
Grade插件其实是一段gradle脚本,实现了Plugin类,通过apply '插件名'
的方式去调用它。而且可以打包上传,复用插件代码。所以叫它插件。
Gradle插件可以用三种方式来写。
第一种方式:直接在build.gradle文件里写。
例如:
apply plugin: MyPlugin
class MyPlugin implements Plugin<Project>{
@Override
void apply(Project target) {
println "Hello,Plugin"
}
}
这种方式的缺点是插件只对当前build script可见,其它脚本无法复用它,不能将插件打包并上传到maven。
第二种方式:创建buildSrc
project
buildSrc
(名字是固定的)是rootProject的子project,它的build.gradle
脚本执行优先级最高,buildScr
project不需要在setting.gradle
里注册。这种方式也适用于本地项目,不适用于将插件打包上传。同时它支持三种语言开发:groovy、java、kotlin。
第三种方式:创建独立的project。
这种方式好处是可以将脚本通过maven插件打包上传,供其它项目依赖。它也支持三种语言开发:groovy、java、kotlin。
下面就以这种方式为例,介绍如何自定义Gradle插件。
新建一个Project,名为GradlePluginDemo
,并新增子project(library)名为pluginlib
,这个project就用来编写Gradle
插件。
创建Gradle Plugin Project
目前Android Studio还不支持新建Gradle Plugin Project,所以新建一个普通的Android Library Module或者Java Library,将它改造成Gradle Module。
先新建一个普通的Android Library Module,按如下操作改造成Groovy Module:
1、将src/main下的目录全删除
2、将build.gradle
文件内容删除,并添加一行 apply plugin: 'groovy'
3、在src/main下新建两个目录:
(1) groovy
或java
或kotlin
。 这里以groovy
为例。
(2) resources/META-INF/gradle-plugins
。
groovy
下放的是源代码,resources
下是资源文件。
改造后的工程目录结构:
自定义Gradle Plugin
在pluginlib中的build.gradle中添加依赖dependencies依赖:
apply plugin: 'groovy'
dependencies{
// gradle sdk
implementation gradleApi()
// groovy sdk
implementation localGroovy()
}
在src/main/groovy下新建包名(比如com.devnn.plugin),并新建插件类,比如PluginImpl.groovy,这个类名无所谓,并不是最后插件的名字。
插件类必须实现Plugin接口,并重写apply方法:
package com.devnn.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
public class PluginImpl implements Plugin<Project>{
void apply(Project project){
//这里写自定义插件实现逻辑
}
}
apply方法是在apply plugin: '插件名称'
这行脚本执行的时候调用。
在这里,我们要实现的功能是将app module的依赖部分写在插件里,并将插件上传到maven私有库。
插件实现代码如下:
package com.devnn.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
public class PluginImpl implements Plugin<Project>{
void apply(Project project){
System.out.println("========================")
System.out.println("apply MyPlugin succeed!")
System.out.println("========================")
project.repositories{
maven{
//添加maven库地址
url 'http://localhost:8081/nexus3/repository/maven-public/'
}
}
//定义project的依赖部分
project.dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.devnn.lib:lib1:1.1.5'
}
}
}
前三行是一个日志打印。这里有一个自定义的名为lib1的library加进了依赖。
在使用插件的module中,需要在build.gradle中添加
apply plugin: '插件名称'
因此需要给我们的自定义插件定义一个名称。
如何定义插件名称?
在src/main/resources/META-INF/gradle-plugins
下面新建一个properties文件,文件名称就是我们的插件名称,比如MyPlugin.properties
,插件名称就是"MyPlugin"。
文件内容中指定插件类的路径:
implementation-class=com.devnn.plugin.PluginImpl
至此,自定义插件的过程就完了。
将插件打包出来就需要写上传代码。可以上传到本地文件仓库,或者maven仓库。两种方式只是上传地址不同。最终形成的插件包是一个jar文件及及pom.xml描述文件。上传到maven仓库需要事先搭建一个maven服务器。
将插件打包并上传到本地文件仓库
如果要在本地测试gradle插件,上传到本地文件仓库是个很好的选择。
这里将本地文件仓库定义在项目根目录的plugins文件夹。此文件夹会自动生成。
本地文件仓库其实就是一个文件夹,里面存放了打包好的jar及其pom.xml,内容跟maven上是一样的,跟maven仓库的区别是仅能供本机使用,不能作为web服务器来用。
首先在pluginlib的build.gradle文件中应用maven上传插件:
apply plugin: 'maven'
然后再添加uploadArchives
任务,完整内容如下。
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies{
// gradle sdk
implementation gradleApi()
// groovy sdk
implementation localGroovy()
}
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = 'com.devnn.myplugin'//这是groudId
pom.artifactId = 'myplugin' //这是artifactId,注意这跟gradle插件的名字是两回事,互不干扰。同一个artifactId可以包含多个插件。
pom.version = '1.0.0'
repository(url: uri('../plugins')) //打包到此脚本上一级目录的plugins目录下(跟pluginlib并列),目录不存在会自动创建。
}
}
}
这里的artifactId跟插件名称容易混淆,它们俩的名字可以相同也可以不同,并不会冲突。一个artifactId是一个依赖包,也是一个jar包,里面可能包含多个插件。就像工程根目录的build.gradle
这行代码:
dependencies {
classpath "com.android.tools.build:gradle:3.6.2"
}
这是gradle脚本需要依赖的插件库(jar包),它是个artifact,artifactId=gradle。这个插件库里面有多个插件比如:com.android.library
、com.android.application
。
这个插件jar包是作用在编译阶段,因为编译程序也是jvm在工作,需要依赖jar。跟平常使用jar包不同,平常使用的jar包作用在app运行时。
上面代码只是写了一个插件,还可以编写多个插件,在资源文件里添加新插件即可:src/main/resources/META-INF/gradle-plugins
添加uploadArchives
任务后在工程根目录执行上传命令即可:
./gradlew pluginlib:uploadArchives
上传成功后就会生成新目录plugins
:
里面就是打包后的产物。
如何使用刚刚打包好的插件呢?
使用时分两步,见以下注释。
buildscript {
repositories {
google()
jcenter()
maven{
url uri('plugins') //1,添加插件仓库地址
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath "com.devnn.plugin:myplugin:1.0.0" //2,添加插件依赖
}
}
第2步插件依赖就是从第1步的仓库地址中去搜索插件。
然后在gradle脚本中添加apply plugin: 'MyPlugin'
,实际就是在调用插件的apply方法。
将插件打包并上传到maven仓库
上传插件到maven仓库,跟上传aar/jar是一样的,在我的另一篇文章中有比较详细讲解:
这里就简单描述一下上传脚本。
首先在pluginlib的build.gradle文件中应用maven上传插件:
apply plugin: 'maven'
添加上传脚本:
def NEXUS_REPOSITORY_URL="http://localhost:8081/nexus3/repository/maven-releases/"
def NEXUS_USERNAME="admin"
def NEXUS_PASSWORD="123456"
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = 'com.devnn.plugin'
pom.artifactId = 'myplugin'
pom.version = '1.0.0'
repository(url: NEXUS_REPOSITORY_URL) {
authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
}
}
}
}
完整的build.gradle文件内容:
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies{
// gradle sdk
implementation gradleApi()
// groovy sdk
implementation localGroovy()
}
def NEXUS_REPOSITORY_URL="http://localhost:8081/nexus3/repository/maven-releases/"
def NEXUS_USERNAME="admin"
def NEXUS_PASSWORD="123456"
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = 'com.devnn.plugin'
pom.artifactId = 'myplugin'
pom.version = '1.0.0'
repository(url: NEXUS_REPOSITORY_URL) {
authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
}
}
}
}
可以跟上面的上传本地文件仓库的脚本对比一下,其实只是上传地址不同。
在终端中执行上传命令:
./gradlew pluginlib:uploadArchives
执行上传命令后,自定义插件类会报红,这是AS自身的问题,不要在意,删除build目录就好了。
到maven私有库上看一下刚刚上传的插件:
可以看到已经上传成功了。
它的pom文件内容:
自动完成插件更新
上面讲解了通过Gradle的uploadArchives命令上传插件到Maven仓库,这个操作可以通过Jenkins帮助我们自动完成。当更新了插件代码后,可以自动触发jenkins打包并上传插件。客户端应用的依赖部分代码(dependencies{…})就完成了云端化。
使用maven仓库中的自定义插件
在工程根目录的build.gradle文件的buildscipt添加插件所在仓库地址,并且添加插件依赖:
buildscript {
repositories {
google()
jcenter()
maven{
url 'http://localhost:8081/nexus3/repository/maven-public/'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.devnn.plugin:myplugin:1.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
在application moudle(app)中应用插件:
apply plugin: 'MyPlugin'
这样就会自动引入了这三个库:
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.devnn.lib:lib1:1.1.5'
application module下面没有配置自己dependencies,全部通过插件来实现。
整个过程的Demo工程源码地址:
https://github.com/devnns/GradlePluginDemo.git