前言
啥是插件?为啥要自定义插件?
其实插件就是一系列特定任务的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