编译自己的Gradle插件
前面几节我们学习了 Gradle 的任务及命令,通过这几节的学习我们已经有了一定的 Gradle 基础,今天我们就来学习一下如何自定义一款 Gradle 插件。我们为什么要自定义 Gradle 插件呢?那当然是为了我们开发方便呀。如果吃力不讨好谁会去做呢。下面我们进入正题。
Gradle 插件主要分为两类:脚本插件和对象插件。下面我们来看下它们的区别。
1. 脚本插件
脚本插件就是我们在.gradle
文件 [例如 demo.gradle ]中定义自己的编译任务。在项目中的build.gradle
文件中通过apply from:'demo.gradle'
就可以使用这个脚本插件。
下面我们以输出项目名称为例子,来简单学习脚本插件,一般我们将脚本插件写在项目的根目录下,项目目录结构如下:
1.1 demo.gradle
我们在这里定义一个任务 showProjectName 输出,调用该插件的 module 的名称:
// demo.gradle
task showProjectName{
doLast {
println("$project.name")
}
}
1.2 在 build.gradle 中引用该插件
我们需要在对应模块的 build.gradle 文件中引用插件,这里我们在 app 模块下的 build.gradle中引用,由于demo.gradle 在 build.gradle 的父目录一级。所以我们需要在前面加上../
,具体如下:
//这行命令需再最前面
apply from: '../demo.gradle'
1.3 运行该任务
我们运行这个任务 ,前面定义 Gradle 任务的时候讲过最好使用驼峰命名,我们可以使用以下命令。
//使用任务全拼
$ gradle showProjectName
//使用简写方式
$ gradle sPN
输出结果如下,我们看到那种结果都会疏忽当前模块的名称 app。
2. 对象插件
所谓对象插件就是指我们定义一个实现org.gradle.api.Plugin
接口的类。这个类就是我们所谓的对象插件。该类必须实现 Plugin 接口的apply
方法。
编写 Gradle 对象插件的方式有以下 3 种:
- 在 gradle 文件中添加脚本: 这种方式就是直接在我们的 build.gradle 文件中添加 Groovy 脚本 。
- 在 buildSrc 目录下创建: 这种方式是在根目录下添加 buildSrc 的一个子模块。
- 在独立项目中创建: 这种方式是创建一个单独的项目,写一个 Gradle 插件,发布后别的项目都可以使用。
2.1 在 gradle 文件中添加脚本
我们还是以上面的输出模块名称为例,我们定义一个PluginInGradleScript
的类实现Plugin
接口:
//app 模块下build.gradle
class PluginInGradleScript implements Plugin<Project> {
@Override
void apply(Project target) {
target.task('showProjectName'){
doLast {
println("PluginInGradleScript:Module Name is $target.name")
}
}
}
}
引用该插件,引用自定义插件时我们使用apply plugin
语句,记住这句还是要在 build.gradle 的最上面两行,具体如下:
apply plugin: PluginInGradleScript
我们到这里就已经将插件引用到项目中了,下面我们还是执行命令,看看定义的插件能否正常输出 module name。
$ gradle showProjectName
> Task :app:showProjectName
PluginInGradleScript:Module Name is app
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
如上所示,我们就成功定义了一个对象插件,这种定义方式适合项目中比较小巧的,简单的一些功能插件。较复杂的还是不建议这种定义方式。
2.2 添加 buildSrc 子项目
我们首先需要在项目的根目录下创建一个 buildSrc 的模块,这样如果项目中有多个模块就可以重复使用插件了。
创建 buildSrc 模块
首先,我们需要创建一个 Java Library 名字叫做 buildSrc。因为插件我们需要使用 Groovy 语言写,所以我们需要将 main 目录下的 java 目录修改为 groovy 目录。
修改后如下所示:
修改 build.gradle
因为我们创建的是 Java Libiary,这里我们使用的是 Groovy 语言所以,buildSrc 的 build.gradle 文件我们需要修改为如下:
//buildSrc/build.gradle
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
CustomPluginInBuildSrc.groovy 文件
紧接着我们需要定义 Plugin 插件,我们还是以输出 module name 为例:
package com.bthvi.buildsrc
import org.gradle.api.Project
import org.gradle.api.Plugin
class CustomPluginInBuildSrc implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('showCustomPluginInBuildSrc') {
doLast {
println("InBuildSrc: Module Name is $project.name")
}
}
}
}
在 app/build.gradle 中引用
我们引用可以使用如下两种方式引用:
第一种: 我们直接按照类名和包名路径引用
import com.bthvi.buildsrc.CustomPluginInBuildSrc
apply plugin: CustomPluginInBuildSrc
//或者直接引用全路径
apply plugin: com.bthvi.buildsrc.CustomPluginInBuildSrc
第二种: 我们按照如下目录创建 resources 目录及 xxx.properties 文件,这里的 xxx 就是我们要引用的插件。我们这里创建 myplugin.properties。并将id对应的 Plugin 实现类全路径配置如下:
implementation-class=com.bthvi.buildsrc.CustomPluginInBuildSrc
使用插件
下面我们使用 gradle 命令来调用这个插件,我们直接使用gradle showCustomPluginInBuildSrc
来执行这个任务。
D:AndroidProjectsCustomGradlePlugins>gradle showCustomPluginInBuildSrc
> Task :app:showCustomPluginInBuildSrc
InBuildSrc: Module Name is app
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed
Tips: 我们可能会遇到
'buildSrc' cannot be used as a project name as it is a reserved name
这个错误,如下图所示。
这个错误的原因是因为,我们在 setting.gradle 中配置了 buildSrc,我们把 setting.gradle 中配置的 buildSrc 删掉就 OK 了。
3. 单独的项目中
上面的 buildSrc 模块下定义 Gradle 插件也只是仅仅限制于当前的项目的各个模块间引用,如果我们想要在多个模块间复用同一个插件,我们就需要单独创建一个工程,并将我们定义的 Gradle 插件发布到 Maven。
单独工程中定义插件跟在 buildSrc 中是一样的,唯一不同的就是我们需要配置上传,这里我们上传到自己的本地目录’loccal’中,这里我从创建一个名字为 CustomPluginDemo,目录结构如下:
我们这里修改 build.gradle 文件如下:
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
group = 'com.bthvi.mplugin'
version = '1.0.0'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('D:/local'))
}
}
}
这里我们配置将插件上传至 D 盘的local
目录下,配置完成我们同步后会看到如下:
我们点击左边三角形执行编译并上传任务,执行完成后我们会在 D 盘local
目录下看到如下目录结构:
这样就代表我们可以在项目中使用此插件了,但是我们需要将本地的 Maven 地址加入项目,在最外层 build.gradle 中配置如下:
buildscript {
repositories {
maven {
url uri('D:/local')
}
google()
jcenter()
}
dependencies {
classpath 'com.bthvi.mplugin:CustomPluginDemo:1.0.1'
classpath 'com.android.tools.build:gradle:3.5.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven {
url uri('D:/local')
}
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
然后像 buildSrc 一样我们就可以在 app 下引用自定义的插件myplugin
了。
apply plugin: 'myplugin'
我们执行gradle showCustomPluginInBuildSrc
为了区分,我们修改了输出语句println("In Projet: Module Name is $project.name")
,我们执行后会看到如下输出:
D:AndroidProjectsCustomGradlePlugins>gradle showCustomPluginInBuildSrc
> Task :app:showCustomPluginInBuildSrc
In Projet: Module Name is app
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
4. 小结
本节,我们从简单到复杂,首先介绍了脚本插件,然后我们介绍了对象插件,先是在脚本中直接定义,但是这种只适用于当前模块,如果一个项目中多个模块想要共用一个插件,我们就需要在 buildSrc 目录下新建一个插件供多个模块复用。但是如果想要多个项目共用一个插件,我们就需要单独定义一个 Gradle 项目,并且上传到 Maven,我们多个项目使用的时候直接添加 Maven 就好。本文中直接上传到本地磁盘了,有兴趣的同学可以下来尝试将自己的插件上传到 Maven 服务器。