再学一次gradle系列——Plugin插件(三)

4,128 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

gradle系列——groovy,核心对象(一)

gradle系列——Task和生命周期(二)

gradle系列——Plugin插件(三)

gradle系列——插件应用Transform(四)

gradle系列——常用技巧(五)

什么是gradle插件

插件的概念可以说无处不在,例如chrome浏览器就有很多扩展插件,用来扩充网页的某种特定的功能,那gradle里面插件是用来干嘛的呢?

其实也一样,用来扩充某种特定的构建能力。gradle是一个构建框架,一定要注意他是一个框架,框架的作用就是定制规则,定制通用的模块,包括如何去管理项目的子模块,控制Task执行的生命周期等,但具体的构建任务,这个就比较多元化了,gradle不仅仅是给Android用的,java项目,web项目也都可以使用gradle去构建,所以针对不同的项目类型,不同的构建任务,gradle提供了插件这一接口,不同的构建任务对应成一个插件,在你需要的时候自己去引入,例如需要构建一个Apk,就引入Android的gradle插件

插件的使用

使用已有的插件主要有4个步骤

  1. 在项目build.gradle中引入插件的仓库,目的是告诉gradle,该项目的插件或者其他三方库可以去哪些仓库里面找
repositories {
        // google这些都是gradle内部已经集成的,所以直接像调用方法一样引入
        google()
        mavenCentral()

    }
  1. 声明导入具体的插件库,一个插件库中可能有多个插件
dependencies {
        // 导入Android构建相关的插件库
        classpath "com.android.tools.build:gradle:4.2.2"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
  1. 在模块module中引入具体的插件
// 引入该插件,会引入打包Apk的一系列Task
apply plugin: 'com.android.application'

插件添加到模块module之后,一般会在当前project下添加两种能力,一种是用于配置的属性,一种是添加不同的Task

  1. 配置插件的属性,这个最常见的就是android闭包了,android闭包就是com.android.application这个插件提供的可配置的属性
android {

    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 19
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

插件属性拓展——gradle DSL

上面android闭包这种代码看起来跟常规的语言不太一样,像是一种配置方式,但其实就是利用了闭包+delegate的方式实现的DSL,我们可以简单实现一个类似的

class Android {
    private int mCompileSdkVersion
    private String mBuildToolsVersion
    private DefaultConfig mDefaultConfig = new DefaultConfig()

    def compileSdkVersion(sdkVersion){
        mCompileSdkVersion = sdkVersion
    }

    def buildToolsVersion(buildVersion){
        mBuildToolsVersion = buildVersion
    }

    def defaultConfig(Closure closure){
        closure.delegate = mDefaultConfig
        closure.call()
    }
}

class DefaultConfig {
    private String mApplicationId
    private int mMinSdkVersion
    private int mTargetSdkVersion

    def applicationId(appId){
        mApplicationId = appId
    }

    def minSdkVersion(minVersion){
        mMinSdkVersion = minVersion
    }

    def targetSdkVersion(targetVersion){
        mTargetSdkVersion = targetVersion
    }
}

def android(Closure closure){
    Android android = new Android()
    // 给闭包设置delegate
    closure.delegate = android
    closure.call()
}

// 调用android函数,就类似上面官方插件提供的android闭包的写法了
android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"
    defaultConfig {
        applicationId "com.dsl.android"
        minSdkVersion 20
        targetSdkVersion 30
    }
}

自定义插件

我们自己也可以通过实现gradle提供的Plugin接口来实现自定义的插件

主要有3种创建插件的位置

  • 直接在gradle脚本中
  • 在buildSrc目录下
  • 创建一个单独的工程

gradle脚本实现

这种方式很简单,例如在moudule的build.gradle文件中写一个插件

class MyPlugin implements Plugin<Project>{

    @Override
    void apply(Project project) {
        println "MyPlugin hello"
    }
}

// 然后可以直接在文件头部apply这个插件,在配置阶段执行到这行代码的时候实际上就会去执行上面重写的apply方法
apply plugin:MyPlugin

这种方式目前没找到实际的用处,意义不太大,了解即可

用buildSrc模块实现

当插件只在当前项目下使用时,可以使用这种方式

自定义插件

  1. 首先新建一个名为buildSrc的module(gradle会默认识别名为buildSrc的目录为项目的插件目录
  2. src目录下仅保留一个空的main文件夹,其他的全部删除,并在main文件夹下新建groovy文件夹和resources文件夹
  3. 在groovy文件夹下创建自定义的一个包名文件夹,例如com.tu.gradle.plugin,注意这个是创建了4层文件夹,然后在plugin文件下新建一个groovy文件CustomGradlePlugin.groovy,实现Plugin接口,并实现它的apply方法
class CustomGradlePlugin implements Plugin<Project>{

    @Override
    void apply(Project project) {
        println "apply hello CustomGradlePlugin"
    }
}
  1. 在resources目录下创建文件夹META-INF.gradle-plugins,注意是两层文件夹,然后在gradle-plugins文件夹下创建一个com.tu.gradle.plugin.properties文件,properties前的文件名部分跟前面创建的包名需要一致,该文件的作用是去指定插件的class
implementation-class=com.tu.gradle.plugin.CustomGradlePlugin
  1. 最后就可以在项目其他moudle中引入使用这个插件了,sync工程之后就能看到在配置阶段执行了自定义插件里apply函数里的代码
apply plugin: 'com.tu.gradle.plugin'

// 输出结果
> Configure project :app
apply hello CustomGradlePlugin

如果一个插件模块想写多个插件,是通过包名去区分的,不同的插件只需要放在不同的包下面

buildSrc的目录结构如图

增加配置属性

上面说了插件的主要功能是向当前project提供了配置属性和task,先自定义一个配置属性

在同一个包目录下新建一个VersionExtension.groovy文件,在里面定义一个实体类,这个实体类里面的属性就是向外提供的可配置属性

class VersionExtension {

    String mVersionName
    int mVersionCode

    def versionName(name) {
        mVersionName = name
    }

    def versionCode(code) {
        mVersionCode = code
    }
}

然后在我们的CustomGradlePlugin得apply方法中,把这个扩展配置属性添加到当前project当中

class CustomGradlePlugin implements Plugin<Project>{

    @Override
    void apply(Project project) {
        println "apply hello CustomGradlePlugin"
        // versionInfo是外部可使用的配置闭包名
        project.extensions.create("versionInfo",VersionExtension.class)
    }
}

这样就可以在引入该插件得模块脚本中去配置使用

versionInfo {
    versionName "1.0.0"
    versionCode 100
}

自定义Task

配置属性的最终目的是需要在插件的Task中去使用,下面在插件中自定义一个Task

同样在一个包目录下新建一个CustomPluginTask.groovy文件

class CustomPluginTask extends DefaultTask {

    CustomPluginTask(){
        // 指定Task得分组
        group = "custom_plugin"
    }

    @TaskAction
    void doAction(){
        // 使用配置参数
        def versionName = project.extensions.versionInfo.mVersionName
        def versionCode = project.extensions.versionInfo.mVersionCode
        println "custom plugin versionName is $versionName and versionCode is $versionCode"
    }

}

然后在CustomGradlePlugin的Apply方法里,把创建的这个task添加到当前project中

class CustomGradlePlugin implements Plugin<Project>{

    @Override
    void apply(Project project) {
        println "apply hello CustomGradlePlugin"
        project.extensions.create("versionInfo",VersionExtension.class)
        // customPluginTask是该Task的名称
        project.tasks.create("customPluginTask",CustomPluginTask.class)
    }
}

最后同步一下,当前module下就能看到这个customPluginTask

执行一下看看

> Task :app:customPluginTask
custom plugin versionName is 1.0.0 and versionCode is 100

到这,一个简单得自定义gradle插件就完成了,是不是很简单

Tips

有的博客说这种方式还要改builsSrc目录下的build.gradle文件,实践发现,这种方式根本就不需要build.gradle文件,我使用的gradle 6.7.1

使用这种方式主要就是供本项目使用,如果想要发布供其他项目使用的话,buildSrc是有一个坑点的,就是buildSrc模块属于是项目保留得一个模块,如果在setting.gradle中去include这个模块得话,就会报错

'buildSrc' cannot be used as a project name as it is a reserved name

但如果想要把插件发布得maven仓库,就必须把这个插件module include到setting.gradle脚本中,这样才能正常使用uploadArchives的task上传maven,所以使用这种方式的应用场景就是只在本项目直接使用(我实验是这样得,但看有得blog说可以,不知道是不是我操作有问题或者是gradle版本问题)

单独的gradle插件工程

这种方式跟在buildSrc模块的方式差不多,主要的区别在于插件module的build.gradle文件,需要去配置依赖的库,源码路径等信息,而且插件module可以任意取名(不能取名buildSrc),实际应用中推荐使用这种方式去做,可操作性比较强

apply plugin: 'groovy'

repositories {
    mavenCentral()
}

dependencies {
    implementation localGroovy()
    implementation gradleApi()

}

sourceSets {
    main{
        groovy {
            srcDir 'src/main/groovy'
        }
        resources {
            srcDir 'src/main/resources'
        }
    }
}

发布插件

把写的自定义插件发布成一个独立的库,这样可方便其他模块使用,发布可以分成两种,本地和远程

发布插件到本地

很简单,利用maven插件,在插件module的build.gradle中引入maven插件,并配置uploadArchives闭包

apply plugin: 'groovy'
apply plugin: 'maven'

repositories {
    mavenCentral()
}

dependencies {
    implementation localGroovy()
    implementation gradleApi()

}

sourceSets {
    main{
        groovy {
            srcDir 'src/main/groovy'
        }
        resources {
            srcDir 'src/main/resources'
        }
    }
}

uploadArchives {
    repositories.mavenDeployer {
        repository(url: uri('../repo'))   // 本地仓库路径
        pom.groupId = "com.tu.gradle.plugin"// group唯一标识,可任意,通常为模块包名
        pom.artifactId = "CustomGradlePlugin"// 插件项目名称,通常为插件库模块名称,可以任意,一个插件库中不同的插件通过不同的包名去区分
        pom.version = "0.0.1"// 版本号
    }
}

配置之后,sync下项目,就能在gradle视图的插件module中看到uploadArchives的task,运行这个task就能把插件发布到本地

发布插件到远程

跟发布插件到本地的差距就是配置相应的远程maven url及用户名密码

uploadArchives {
    repositories.mavenDeployer {
        repository(url: REMOTE_MAVEN_URL) {                 authentication(userName: "tu", password: "123456")             
        }
        pom.groupId = "com.tu.gradle.plugin"// group唯一标识,可任意,通常为模块包名
        pom.artifactId = "CustomGradlePlugin"// 插件项目名称,通常为插件库模块名称,可以任意,一个插件库中不同的插件通过不同的包名去区分
        pom.version = "1.0.0"// 版本号
    }
}

使用发布的插件

发布之后,就可以像平时使用三方插件一样去使用

先在根项目下声明引入插件

repositories {

        // 本地maven相对地址
        maven {
            url 'repo'
        }

    }
    
dependencies {
        classpath "com.android.tools.build:gradle:4.2.2"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.tu.gradle.plugin:CustomGradlePlugin:1.0.0"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

然后在子module如app模块下使用

apply plugin: 'com.tu.gradle.plugin'

以上就是gradle插件相关知识的分享,如果有写的不对的地方欢迎批评指正,感觉写的还不错的也欢迎评论点赞支持一下