Gradle自定义插件初探

212 阅读6分钟

通过使用gradle插件,我们就可以在编译打包期间做一些操作,常见的比如字节码插桩等。

如何使用插件?

首先需要在根目录下的build.gradle文件中的buildscript块中引入插件,也就是所谓的依赖,自带的插件不需要在此配依赖,第三方的插件才需要。需要先配置maven,然后通过classpath的方式引用。

image.png

然后在想使用该插件的模块中应用此插件,具体操作是在对应模块的build.gradle文件中通过apply plugin: '插件id'的方式使用。

image.png

自定义插件

有时候官方的插件并不能满足我们个性化的需求,就需要自己去开发gradle插件,比如实现按钮点击防抖,那么就可以通过插件在编译打包期通过字节码插桩的方式插入我们的代码,就不需要手动地给每一个点击方法加代码,一来费工夫,二来增加了我们看到的代码量,一堆重复代码,总不是那么美观。

自定义插件有三种实现方式:

  1. 直接在模块的build.gradle文件中写,这种方式开发的插件只能在本项目中使用。也称为Build Script方式。
  2. 在项目的根目录下新建个buildSrc文件夹,在里面去写插件内容,这种方式的插件只能项目内使用。也称为buildSrc project方式。
  3. 新建一个模块,在新的模块下写插件内容,可以将模块发布到maven供其他项目使用。也称为Standalone project方式。

1、Build Script方式实现插件

此方式会自动编译并包含在构建脚本的类路径中,无需执行任何操作。在build.gradle中实现一个插件,然后apply调用即可。该插件在构建脚本之外是不可见的,因此不能在定义它的构建脚本之外重用该插件。

image.png

apply plugin: MyBuildPlugin

class MyBuildPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println 'Hello from the MyPlugin'
            }
        }
    }
}

在上面的代码中,自定义的插件要实现Plugin<Project>接口,并实现apply方法,此方法是插件的入口。我们注册了一个叫hello的任务,并且在任务的最后打印一行文字。

编写好代码后进行sync同步,同步后AS右侧的gradle面板中就会出现一个hello任务,具体路径在Tasks->other中。

image.png

image.png

双击hello任务,控制台就会输出我们写的内容。

image.png

2、buildSrc project方式实现

这种方式实现的插件对项目中的每个构建脚本都是可见的,但是对项目外不可见。

首先需要创建项目根目录/buildSrc/src/main/java目录,这目录是存放Java代码的,当然,如果你不想用Java写,用Kotlin或者Groovy写也可以,创建对应的目录就行了(项目根目录/buildSrc/src/main/groovy项目根目录/buildSrc/src/main/kotlin根据你喜欢的语言)。创建文件夹之后sync同步,gradle就会对buildSrc做一些处理,生成一些gradle相关的文件。

image.png

在Java目录下新建一个Java文件,在里面写插件内容:

import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;

public class MyBuildScrPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.task("srcTask").doLast(new Action<Task>() {
            @Override
            public void execute(Task task) {
                System.out.println("I am BuildScrPlugin!");
            }
        });
    }
}

同样在需要用到的模块的build.gradle中调用一下,apply plugin: MyBuildScrPlugin,然后同步,在gradle面板中找到我们注册的srcTask任务双击。

image.png

3、Standalone project模式创建插件

把插件放到一个独立的模块中去实现,这个模块必须是一个Java或Kotlin模块,这样就能将模块打包成jar包供其他项目使用,这种方式才是最常用的插件开发。 image.png

由于前面的两种插件实现方式都是在gradle的“势力范围”内,所以我们不需要额外引入什么东西,但是现在独立一个模块,就得引入:implementation gradleApi(),不然Plugin<Project>就找不到了。 打包和发布插件的最简单和推荐的方法是使用Java Gradle Plugin Development Plugin,也就是Gradle插件开发插件,专门服务于插件开发的插件。 所以需要在插件模块的build.gradle中加入一下信息: image.png

如果报错: Build was configured to prefer settings repositories over project repositories but repository 'Gradle Libs' was added by unknown code

则将setting.gradle中的repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)注释掉。

创建Java文件,写插件内容:

import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;

public class CustomPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.task("customPlugin").doLast(new Action<Task>() {
            @Override
            public void execute(Task task) {
                System.out.println("I am CustomPlugin!");
            }
        });
    }
}

Java文件写好了,那么还需在build.gradle中去声明插件信息,以告诉插件打包插件(Java Gradle Plugin Development Plugin)相关的信息,指明我们写的Java文件。

gradlePlugin {
    plugins {
        // 声明插件信息,这里的 myCustomPlugin 名字随意
        myCustomPlugin {
            // 插件ID
            id = 'com.example.plugin.myplugin'
            // 插件的实现类
            implementationClass = 'com.example.plugin.CustomPlugin'
        }
    }
}

这样插件就写好了,那么接下来就要把这个模块打包成jar,给其他项目用,我这里直接用发布到本地maven的方式,方便又快捷。

首先需要引入maven插件:

plugins {
    ... 
    id 'maven-publish' 
}

然后定义发布相关的信息:

publishing {
    publications {
        // 这里myHub可以任意命名,是本地仓库名称
        myHub(MavenPublication) {
            // 插件的组ID,建议设置为插件的包名
            groupId = 'com.example.plugin'
            // 插件在maven中的id,在通过classpath(groupId:artifactId:version)引用的时候需要填
            artifactId = 'myMaven'
            version = '0.0.1'
            // 组件类型,我们的插件是Java组件
            from components.java
        }
    }
    repositories {
        maven {
            // $rootDir 表示本项目的根目录
            url = "$rootDir/repo"
        }
    }
}

同步之后,我们看到gradle面板上多了个publishing任务组。

image.png 根据名称找到对应的任务双击,将插件发布到我们指定的本地目录。

image.png

可以看到发布之后在项目根目录下生成了本地仓库。

接下来我们在其他项目调用这个插件测试看看。

image.png

image.png

同步之后我们看到gradle面板中多了一个我们自定义的任务:

image.png

双击看看效果

image.png

完美!

可能又会有同学要问:作为其他项目的开发,我调用了你的插件,但是不想打印你写的内容,要能支持调用者自定义打印内容,要怎么改呢?

其实也很简单,新建一个类用来承载数据:

image.png

再修改一下插件类:

import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;

public class CustomPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        // 利用Extension创建setPrint闭包,用于接受外部传递的参数值
        final MyExtension extensions = project.getExtensions().create("setPrint", MyExtension.class);
        project.task("customPlugin").doLast(new Action<Task>() {
            @Override
            public void execute(Task task) {
                System.out.println(extensions.getMessage());
            }
        });
    }
}

已经调整好了,重新发布一下,第三方项目就可以自己配置打印内容了:

image.png

运行一下看看效果:

image.png

好了,gradle插件的内容就先介绍到这里,如果对你有帮助,点赞收藏关注走起!