Gradle插件的使用

1,781 阅读4分钟

前言

Gradle框架只是定义了通用的构建流程,并没有实现具体构建任务,Gradle好比一个抽象类,Java构建,c++构建,Android构建则是一个个子类实现,交给开发者自行定义。Gradle插件就是开发者自定义构建逻辑的入口。

当掌握了Gradle基础之后,Gradle插件的定义并不难,难的是如何在实际情况中使用,设计自己的任务。

我了解到的Gradle插件基本上都是基于AGP插件的Transform api 结合字节码插装使用,本文主要介绍插件的定义

Gradle Plugin 概述

要编写Gradle插件的需要实现Plugin接口,将插件应用于项目时,Gradle会创建插件的实例,并调用插件的Plugin.apply()方法。 把项目对象当作参数传递。

Plugin接口是泛型类,可以设置插件的类型为:Gradle,Setting,Project(Gradle三个核心对象)

分别对应三种配置文件

Gradle—init.gradle

Setting—settings.gradle

Project—build.gradle

如果Plugin 在settings.gradle 被引入 编译时会报错的,因为类型错误。

Gradle Plugin像是一种对当前 gradle脚本的代理。 插件中编写的代码,配置文件中都可以定义。区别在于插件提供了一种脚本逻辑的封装方式,把逻辑编写在插件里复用性更高。

定义插件有三种方式, 三种定义方式的区别在于,插件的作用域。

  1. 在脚本中定义插件
  2. buildSrc项目
  3. 独立项目

当前有个多模块工程,具有模块A,B,C。 在模块A的build.gradle 文件中 编写插件 或 引入单独的 xxx.gradle文件。那么 插件逻辑只会在模块A生效,B,C模块不受影响。

如果定义buildSrc项目,那么模块A,B,C都会受插件逻辑的影响。

如果为插件创建独立项目,生成并发布一个jar包,那么就在任意项目中使用。

Plugin :hello world

演示在脚本中创建插件 并 创建任务 输入 hello world


------------------demo.gradle-------------

//创建插件
class ProjectPlugin implements Plugin<Project>{

    @Override
    void apply(Project project) {

        project.task("hello"){
            println "配置阶段 任务名称 输出: hello"
            doLast {
               println "执行阶段 hello world"
           }
        }
        println "配置阶段 输出:${project.name}"
    }
}

//应用插件
apply plugin: ProjectPlugin

--------------------任意项目下 build.gradle----------------------
apply from: "../demo.gradle"

--------------------命令行执行 查看输出----------------------
gradle  hello

定义个插件还是非常简单的,细节知识还是挺多的。

Gradle构建生命周期分三步:初始化,配置,执行。

所谓配置阶段 就是将项目内所有脚本文件执行一遍,创建任务。

执行阶段 实际是执行在配置阶段创建的任务。 每个Project中持有 TaskContainer

用Java线程池概念类比,TaskContainer 就是一个线程池。TaskContainer 中保存任务。线程池中保存Runnable

配置阶段 约等于 向线程池中添加 Runnable 。 添加任务后 在一个恰当的时机 执行提前创建的任务,就是Gradle的执行阶段。

Extension扩展(插件传参)

接下来将会看到 熟悉的 android配置原理

android {
    compileSdk 32

    defaultConfig {
        minSdk 23
        targetSdk 32
        versionCode 1
        versionName "1.0"
    }
}
------------------demo.gradle-------------

interface ProjectExtension {
    Property<String> getName()
    Property<Integer> getCode()
}

class ProjectPlugin implements Plugin<Project>{

    @Override
    void apply(Project project) {
        def user = project.extensions.create("proExt",ProjectExtension.class)
        project.task("hello"){
            println "配置阶段 任务名称 输出: hello"
            doLast {
               println "执行任务 hello world"
               println "执行任务 输出扩展信息 name:${user.name.get()}    age:${user.code.get()}"
           }
        }
        println "配置阶段 输出:${project.name}"
    }
}

apply plugin: ProjectPlugin

--------------------任意项目下 build.gradle----------------------
apply from: "../demo.gradle"

proExt {
    name = "小明"
    code = 123
}

--------------------命令行执行 查看输出----------------------
gradle  hello

定义接口,声明返回类型为 Property 的抽象方法, 定义普通JavaBean也可以达到同样的效果

需要强调的是,这两个小demo,都是在操作 void apply(Project project) {} apply方法传入的Project参数。

build.gradle中同样可以调用 extensions.create()task() 方法。

Project 代表插件依赖的项目 。Project 是个接口, 每个build.gradle都会生成一个Project对象,代表当前项目。

涉及到对于Gradle脚本的理解问题 build.gradle 约等于 Project的实现类。子类自然可以随意调用Project暴露的方法

运行下方代码,创建任务hello2,可以看到在插件中创建任务同样的效果。

注意!!! 插件也好 脚本也罢 所有的操作 本质上都是操作 Project对象

--------------------任意项目下 build.gradle----------------------

task("hello2"){
    doLast {
        println "我是 ${project.name} 的任务 hello2"
    }
}
--------------------命令行执行 查看输出----------------------
gradle  hello2

buildSrc项目

创建一个Java项目,项目名必须是 buildSrc ,不需要在settings.gradle中配置include导入,创建项目后要注释掉,不然会提示重复导入。

buildSrc 是gradle的一个保留名称,Gradle会自动识别并导入项目,把它当作插件项目运行,编译在其他子项目之前。

项目加载完毕之后,需要声明插件,

  1. 创建resources 目录
  2. 创建META-INF目录
  3. 创建gradle-plugins 目录
  4. 创建xxx.properties 配置文件,xxx会被外部识别为插件名字,比如:我们使用Android插件名称是 com.android.application ,在AGP源码中配置文件被声明为com.android.application.properties
  5. 在配置文件中声明插件实现类 implementation-class=com.example.plugin.DemoPlugin

这样一个插件就配置好了,

buildSrc项目,可以用groovy,java,kotlin实现都可,demo使用Java

Untitled.png

具体代码如下,与脚本实现插件并没有什么区别

public class DemoPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.getExtensions().create("demoUser", UserExtension.class);
        project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                UserExtension extension = project.getExtensions().getByType(UserExtension.class);
                System.out.println(extension.toString());
            }
        });
    }
}

public class UserExtension {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserExtension{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

-----------任意 build.gradle-----------

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.example.demo' 
}

demoUser{
    name = "xixixi"
    age = 12
}

总结与参考

总体上说,单纯的写个可运行的Gradle插件还是比较简单没多少东西,难的是如何把插件机制和业务需求结合起来,emm 一直混小厂没见过啥相关案例,就不多说了。发布插件 上传maven没实践过也不多说了 有需要的同志自己研究一哈吧

开发自定义 Gradle 插件 官方文档浏览器机翻一下还是能看的