美文网首页
自定义Gradle 插件及遇到的问题

自定义Gradle 插件及遇到的问题

作者: wind_sky | 来源:发表于2019-08-07 16:07 被阅读0次

    以自定义一个TestPlugin 的插件为例,记录一下遇到的坑。

    一. 自定义Gradle 插件的步骤

    1. 创建一个插件的项目

    本例是在一个project 里创建一个module,module 名就叫做testplugin。创建完之后,目录结构可能跟plugin 所需的有些不一致,可以先将无用的目录删除掉,然后在main 文件夹下创建groovy 文件用来保存groovy 代码,

    同时在main 目录下创建resources 目录,在resource 目录下创建META-INF 目录,在META-INF 目录下创建gradle-plugins 目录,啰嗦了这么多是因为这块有坑。最后形成的文件结构如下图所示:

    image.png
    :注意箭头所指处,META-INF/gradle-plugins 被简写成了这种样式,这个我们也都明白,但是注意在新建目录时不要直接写成这种 "点" 的形式,因为 "点" 也会被作为目录名而不是下级目录,但是在新建包时是可以直接写点的。

    在gradle-plugins 目录下创建xxx.properties 文件,这个文件的意义就是为我们自定义的plugin 声明了id 为xxx。当我们在使用plugin 时会有这样一句:

    apply plugin: 'xxx'
    

    上面这句的 xxx 就是plugin 的 id。另外xxx.properties 的内容如下:

    implementation-class=com.whx.testplugin.TestPlugin
    

    后面的class 根据自己的实现来改。

    2. 配置gradle 文件

    上图中的build.gradle 文件,为了能开发插件,需要进行相关配置:

    // 应用groovy,plugin 开发是基于groovy 语言的
    apply plugin: 'groovy'
    // 应用maven,用于将插件上传至maven 仓库
    apply plugin: 'maven'
    
    buildscript {
        repositories {
            jcenter()
            mavenCentral()
        }
    }
    
    repositories {
        jcenter()
        mavenCentral()
    }
    
    // 定义组,发布plugin 使用
    group='com.whx.testPlugin'
    // plugin 版本
    version='1.0.0'
    
    // 一个将plugin 上传到maven 仓库的Task
    uploadArchives {
        repositories {
            mavenDeployer {
                // 定义插件打包后上传的位置,可以随意指定,但是在使用时需要指定同样的文件才能找到
                // 这里指定的是本地相对路径
                repository(url: uri('../repo'))
            }
        }
    }
    
    dependencies {
        compile gradleApi()     // groovy API
        compile localGroovy()   // groovy API
        compile 'com.android.tools.build:gradle:3.0.0'      // 如果Android 编译相关,需依赖这个
    }
    

    配置了gradle 文件之后,就可以进行plugin 的开发了,在定义了uploadArchives 之后,会在testplugin 这个module 下产生一个名为uploadArchives 的Task,双击即可运行


    image.png

    这里指定的上传地址是本地地址,uploadArchives 运行成功之后就会在本地自定义的路径下看到一些文件,如图


    image.png
    1.0.0 是版本号,上级路径是包名,这里省略了,生成了这些文件,说明我们的插件生成成功了。

    3. 编写代码

    要实现plugin 的功能,需要创建一个类,实现Plugin<T> 接口,及apply 方法:

    class TestPlugin implements Plugin<Project> {
    
        @Override
        void apply(Project project) {
    
            // 创建一个extension
            project.extensions.create("testPlugin", TestPluginExtension)
    
            // 创建一个task
            project.task('hello') {
                doLast {
                    println("Hello ${project.testPlugin.filePath}")
                }
            }
        }
    }
    

    上面提到了创建一个extension,这个类的作用就是为插件提供一些可配置的属性,也就是我们可以从使用插件的模块的gradle 文件配置属性值来供插件使用。

    class TestPluginExtension {
        String filePath
    }
    

    \color{#ff5533}{坑2}: 在新建groovy 文件时,如果采用 new → File → 文件名 → Register New File Type Association(选groovy)的方式创建groovy 文件有时候是不会为文件加 ".groovy" 后缀名的,如果没有后缀名,那么编译时就不会将这个文件编译,编译器不会报错,但是生成plugin 之后,在使用plugin 时就会报找不到某个类的错误。所以在新建一个文件之后要注意检查是不是有正确的后缀名,不要过于相信IDE。

    上面的plugin 类中,除了创建一个extension 来定义可配置属性外,还创建了一个Task,如注释所写。上面这种方式是在plugin 类里直接定义了一个Task 的简单方式,当然也可以把Task 抽出来写成一个独立的类

    class MagicTask extends DefaultTask{
    
        String filePath
    
        @TaskAction
        def taskMethod() { // task 要执行的任务代码
    
            filePath = project.testPlugin.filePath  // 1
    
            new SolveFile().solve(filePath)         // 2
        }
    }
    

    上面的filePath 的值可以直接从project 对象获取,如1处所示,由于groovy 也是基于JVM 的,也像Kotlin 一样能够无缝调用Java 代码,2处代码实际就是调用的Java 类的方法。

    4. 使用自定义plugin

    使用plugin 也比较简单,首先在Project 级的build.gradle 配置一下依赖

    build.gradle(Project 级)

    buildscript {
        ...
        repositories {
            ...
            maven {
                url uri('./repo')
            }
        }
        dependencies {
            ...
            classpath 'com.whx.testPlugin:testplugin:1.0.0'
        }
    }
    

    由于我们自定义plugin 时是上传的maven 仓库,所以在使用时的依赖库也要用maven,并用url 来指定地址。

    \color{#ff5533}{坑3}:由于是本地的相对路径,注意路径的正确性,另外dependencies 中要使用classpath 声明,后面的形式是 “包名:module 名:版本号”。注意这些配置如果放在模块级 build.gradle 文件的话,maven 的url 地址可能会覆盖project 中的配置,从而导致依赖找不到等奇怪错误。

    接下来,在我们要使用的模块的 build.gradle 文件中应用plugin 并配置属性:

    build.gradle(module 级)

    apply plugin: 'testPlugin'          // 1
    
    testPlugin {                        // 2
        filePath = projectDir.absolutePath + '/src'
    }
    ...
    

    第一行就是应用我们的插件,第二行就是配置一些属性值来供插件使用,这些属性就是我们在TestPluginExtension 中定义的。

    \color{#ff5533}{坑4}:第一行,应用的plugin ,值是我们自定义plugin 的id,这个值就是上面说的xxx.properties 的xxx,如果没有创建这个properties 文件,那么这个id 默认是实现了Plugin<T> 接口的类的全限定名。

    至此,一个简单的自定义插件就完成了,只要避过了上面的几个坑就基本能跑通了。build 一下项目之后,会生成我们在plugin 类中声明的task,在Project 下,进入命令行输入

    ./gradlew taskName
    

    就会执行Task。

    另外关于自定义plugin,还有一些其他内容,包括groovy 的语法特性,gradle 的运行过程,plugin 的调试等,由于篇幅限制就不一一介绍了,这里就简单介绍一下gradle 的构建过程:

    gradle构建的过程总共分为三个阶段:初始化阶段、配置阶段、运行阶段。

    • 初始化阶段是执行settings.gradle文件中的内容,看看这个Project需要构建哪几个module。

    • 配置阶段是从根Project依次遍历module,并为每个module生成一个Project对象,配置阶段完成时就形成了一个完整的task依赖图,同时配置阶段还会执行Task/Plugin 的初始化方法,定义期语句,configure 语句等,但不会执行真正要执行的内容。

    • 执行阶段就是执行Task,执行阶段不依赖脚本的定义顺序。

    那么apply方法是什么时候执行的呢?
    是在配置阶段遇到apply plugin:'testPlugin' 就开始执行, apply方法中传入的Project对象就是某个使用该插件的Project的对象。

    二. 自定义gradle 插件的建议

    可以看到plugin 就是一个 Plugin + Extension + Task 的一个模式,接收用户配置执行特定Task,所以我们可以把Plugin 类作为一个载体,只负责创建Extension 和Task,而不要实现业务逻辑;同时如果Task 比较多的话,可以创建一个辅助类TaskManager 来管理Task,另外Task 也是groovy 文件,如果我们对groovy 不熟悉,可以在Task 中调用Java 代码来实现功能。

    相关文章

      网友评论

          本文标题:自定义Gradle 插件及遇到的问题

          本文链接:https://www.haomeiwen.com/subject/fozodctx.html