自定义Gradle Plugin

前言

啥是插件?为啥要自定义插件?

其实插件就是一系列特定任务的Task,这些Task具有特殊的功能,比如安卓工程的默认gradle插件就有好多Task:build、clean等。目前使用AS编译的安卓工程都是基于Gradle构建的。Gradle的灵魂就是Task。一般这些task 参与、干预项目的打包流程。因此我们自定义插件也可以自定义一些Task来干预项目的打包。

插件的应用场景?

接下来我们就回顾下安卓工程中的gradle插件场景,如下部分代码。是不是很熟悉又陌生,熟悉是因为你创建工程后、项目开发中经常看到但是一般经常写些java源代码,这些默认的脚本一般也不用管。陌生就是。。。。好吧自己也不知道为啥系统默认这样加。
本文就来介绍下自定gradle插件的相关知识。

//1、工程的build.gradle 文件中
 dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
      
    }
    //2、app的build.gradle 文件中
apply plugin: 'com.android.application'

自定义Gradle Plugin

gradle插件的分类

1、脚本插件:一般为新建个xxx.gradle文件,内部写Task, 然后通过apply from xxx.gradle
2、对象插件:一般为groovy源文件中自定义类继承Plugin实现apply接口。相对脚本插件扩展性强,功能强大。

自定义gradle plugin对象插件的几种写法

1、直接在 build.gradle 文件中写
2、新建 buildSrc 工程
3、新建 Java Library 工程

(1)直接在 build.gradle 文件中写

1、这种方式是最简单的写法,直接在module的build.gradle文件中自定义类实现plugin接口即可。代码如下。
2、Task的执行使用gradle 命令或者右侧Gradle窗口双击task
3、弊端:

// 自定义插件
class CustomPlugin implements Plugin<Project>{
    @Override
    void apply(Project target) {
        target.task("newTask"){
            println('i am custom plguin !')
        }
    }
}

apply plugin: CustomPlugin //引入自定义插件

(2)新建 buildSrc 工程

由于这种方式定义的 Plugin 只能在当前工程使用,不能像我们使用android gradle plugin、butterknife plugin 那样任意工程都能使用,有弊端所以这里就不在介绍。

(3)新建个java library

这种方式就是本节的重点,扩展性很强,接下来就重点总结下。

新建java Library 方式

1、新建个java library

如下图,新建个java lib,名字为myplugin

在这里插入图片描述

2、脚本添加依赖

build.gradle下添加些必要依赖。

apply plugin: 'groovy'
apply plugin: 'java-library'
apply plugin: 'maven'

dependencies {
    //Gradle Plugin 依赖
    implementation gradleApi()
    //本地发布 Plugin
    implementation localGroovy()
}
repositories {
    mavenCentral()
}
3、创建groovy、resources文件夹

在这里插入图片描述

如上图创建groovy,和resources包。当我们第二步gradle脚本重新sync下后,这里创建的这两个包会被编译器识别的。

4、自定义个plugin

在groovy包下新建个包(可自定义这里为com.sunnyday.appclick_aspectj_aop),然后自定义类书写插件逻辑即可。

package com.sunnyday.appclick_aspectj_aop

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.task("CustomTask"){
            println("i am custom task!")
        }
    }
}
5、添加配置文件

参看上图。在resources目录下,创建目录META-INF/gradle-plugins。然后我们在resources/META-INF/gradle-plugins目录下再创建一个后缀名为.properties的文件,用来声明插件名称以及对应插件的包名和类名。
ps:
1、目录META-INF/gradle-plugins为固定文件夹,字母不要写错啦。
2、如下图配置文件的内容为固定格式,引入自定义的插件类即可。不过需要注意的是配置文件的名字,这里我定义为包名(com.sunnyday.appclick_aspectj_aop) ,文件名是可以随意定义的,但是最好能是包名,因为这个名字就是我们自定义插件的id我们引入插件时就是apply plugin +插件id方式

在这里插入图片描述

6、发布到仓库

这时我们的自定义插件还不能被我们的工程使用,还需要发布插件到远程仓库(如maven仓库)或者本地仓库。

apply plugin: 'groovy'
apply plugin: 'java-library'
apply plugin: 'maven'

dependencies {
    //Gradle Plugin 依赖
    implementation gradleApi()
    //本地发布 Plugin
    implementation localGroovy()
}
repositories {
    mavenCentral()
}
//本地发布,发布到根目录的 /repo 文件夹下
uploadArchives{
    repositories {
        mavenDeployer{
            //本地仓库路径。放到项目根目录下repo文件夹下
            repository(url :uri('../repo'))
            pom.groupId = "com.sunnyday.appclick_aspectj_aop"
            pom.version = '1.0.0'
        }
    }
}

(1)插件的组成

还是安卓工程的栗子,我们看看安卓gradle插件的组成。如下,其实classpath后面的内容由三部分组成:
1、groupId,对应下com.android.tools.build
2、artifactId,对应下gradle
3、version,对应下3.5.3

 classpath 'com.android.tools.build:gradle:3.5.3'

(2)解释

1、如上代码就是发布到本地仓库的代码。
2、可能你会疑问 classpath groupId:artifactId:version这就是classpath后面的语法结构吧,当是为啥上述我们的代码中没artifactId? 其实artifactId有默认值,默认值为module名即上文的myplugin

(3)发布本地

上述代码配置好后sync下我们就可进行发布了。有两种方式进行发布:
1、编译器gradle窗口双击uploadArchives task
2、gradle 命令: gradlew uploadArchives
ps:至于发布远程仓库参考官网

在这里插入图片描述

7、使用插件

这一步走过后你就会豁然开朗了,为啥 引用classpath xxx, apply plugin xxx的写法了。。。

(1)声明插件:项目的build.gradle 文件下

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        maven{// 添加本地仓库路径
            url uri('./repo')
        }
        mavenCentral()
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'com.sunnyday.appclick_aspectj_aop:myplugin:1.0.0'// 声明插件
    }
}

allprojects {
    repositories {
        maven{ // 添加本地仓库路径
            url uri('./repo')
        }
        mavenCentral()
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

(2)引入插件

apply plugin: 'com.sunnyday.appclick_aspectj_aop' // app的build.gradle文件中

特别注意:
走到这里一般就是一只脚迈进成功的门槛啦!但是这里最容易出现错误,can not load class XXX,一般出现这个错我的原因是你的自定义插件类没打进jar包,你翻翻repo文件夹,解压jar包看下。如果没打进jar包的话你就仔细梳理下自定义插件的流程看看哪些步骤错了没?
提示:
1、检测自定义插件类的包导入了没,你不导入编译器也不报错。
2、检测配置文件配置的类名对不?是否为自定义插件的。
3、其他原因自己仔细找找吧,重新发布下再看看。。。

8、插件更新

如需更新插件,代码更改后重新发布即可。

Plugin Project

上述的自定义插件中有个Project参数,这个参数十分重要,比如某个module apply了我们的插件,这个Project就代表当前module。其中,Project与build.gradle是一对一的关系。所以使用这个参数我们还可以访问使用插件module的build.gradle文件内容。接下来我们就了解下project访问build.gradle中的闭包扩展.

1、情景再现

你或许会发现app build.gradle文件下有好多闭包诸如android{} ,dependencies{}等,安卓插件是如何读取这些闭包信息的呢?其实就是通过自定义插件的Project参数访问的。

2、Project访问 build.gradle 的Extension栗子

(1)定义个扩展类(闭包)

定义个扩展类,用于控制是否为debug模式

package com.sunnyday.appclick_aspectj_aop

/**
 *自定义 extension
 * */
class PluginConfig {
    boolean debug //是否开启调试模式开关
}

(2)修改之前的自定义插件代码

package com.sunnyday.appclick_aspectj_aop

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {

        project.extensions.create("MyPlugin",PluginConfig)//读取用户在app build.gradle下的MyPlugin闭包

        project.task("CustomTask"){
            println("i am custom task!")
        }

        project.afterEvaluate {
          println("debug="+project.MyPlugin.debug)
        }
    }
}

project.extensions.create方法参数

参数1:String类型,自定义闭包名,这里定义后,你在其他工程写闭包要用这个名字
参数2:配置类,关联你自定义的配置类即可。

(3)使用

使用之前要记得重写发布下插件!

// 使用,这里配置下,上文自定义插件中就可以读取到我们配置的值啦!
MyPlugin{
    debug = true
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation 'org.aspectj:aspectjrt:1.8.9'//aspectj
}

end

学到了总结下!!!

参考:微信读书-《安卓全埋点解决方案》第8章 $AppClick全埋点方案5:AspectJ

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值