编译自己的 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 种:

  1. 在 gradle 文件中添加脚本: 这种方式就是直接在我们的 build.gradle 文件中添加 Groovy 脚本 。
  2. 在 buildSrc 目录下创建: 这种方式是在根目录下添加 buildSrc 的一个子模块。
  3. 在独立项目中创建: 这种方式是创建一个单独的项目,写一个 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:\AndroidProjects\CustomGradlePlugins>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:\AndroidProjects\CustomGradlePlugins>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 服务器。