Using the Gradle plugin

foreword

The Gradle framework only defines the general construction process, but does not implement specific construction tasks. Gradle is like an abstract class, Java construction, C++ construction, and Android construction are implemented by subclasses, which are handed over to developers to define themselves. The Gradle plugin is the entry point for developers to customize build logic.

After mastering the basics of Gradle, the definition of Gradle plugins is not difficult, but how to use them in actual situations and design their own tasks.

The Gradle plugins I learned are basically based on the Transform api of the AGP plugin combined with bytecode instrumentation. This article mainly introduces the definition of the plugin.

Gradle Plugin Overview

To write a Gradle plugin, you need to implement the Plugin interface. When the plugin is applied to a project, Gradle will create an instance of the plugin and call the plugin's Plugin.apply() method. Pass the item object as a parameter.

The Plugin interface is a generic class, and the types of plugins that can be set are: Gradle, Setting, Project (three core objects of Gradle) .

Corresponding to three configuration files

Gradle—init.gradle

Setting—settings.gradle

Project—build.gradle

If the Plugin is imported in settings.gradle, it will report an error at compile time, because of the wrong type.

The Gradle Plugin acts as a proxy for the current gradle script. The code written in the plug-in can be defined in the configuration file. The difference is that the plug-in provides a way of encapsulating script logic, and writing the logic in the plug-in is more reusable.

There are three ways to define a plug-in. The difference between the three definition methods is the scope of the plug-in.

  1. Define plugins in scripts
  2. buildSrcproject
  3. independent project

There is currently a multi-module project with modules A, B, and C. Write a plugin in the build.gradle file of module A or introduce a separate xxx.gradle file. Then the plugin logic will only take effect in module A, and modules B and C will not be affected.

If you define a buildSrcproject, then modules A, B, C will all be affected by the plugin logic.

如果为插件创建独立项目,生成并发布一个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 插件 官方文档浏览器机翻一下还是能看的

Guess you like

Origin juejin.im/post/7118260546440757279