Android 自定义Gradle插件的3种方式
前言
Gradle插件在Android中的应用很广泛,很多字节码插桩方案就用到了这方面的知识,Android官方提供了很多可用的插件,比如
apply plugin: 'com.android.application'
:它表示生成一个apk应用的插件;apply plugin: 'com.android.library'
:它表示生成AAR包。
本文只是为入门Gradle插件提供一些思路与实践方案,不深入解析Gradle工作原理和Task相关的内容。
Gradle插件官方文档地址:
https://docs.gradle.org/current/userguide/custom_plugins.html
Gradle Plugins简介
Gradle插件打包了可重用的构建逻辑,可以在不同的项目中使用。Gradle提供了几种方式来让你实现自定义插件,这样你可以重用你的构建逻辑,甚至提供给他人使用。
可以使用多种语言来实现Gradle插件,其实只要最终被编译为JVM字节码的都可以,常用的有Groovy
、Java
、Kotlin
。通常,使用Java或Kotlin(静态类型)实现的插件比使用Groovy实施的插件性能更好。
打包插件的3种方式
- Build script:在
build.gradle
构建脚本中直接使用,只能在本文件内使用; - buildSrc project:新建一个名为
buildSrc
的Module使用,只能在本项目中使用; - Standalone project:在独立的Module中使用,可以发布到本地或者远程仓库供其他项目使用。
Build script
直接在构建脚本中编写插件代码,并应用插件。比如说在app
的build.gradle
中加入如下代码:
按照官方文档的做法,build.gradle
内引入上面的代码,会发现Plugin
和Project
2个类是无法被引入的。而且这种方案有个弊端,只能在构建脚本文件内部使用,这样就没办法提供给其他module
或者project
使用了。这种方案基本不会在真实项目中使用
注意:对于Gradle
而言,每一个Module
都是一个项目。先知道这个概念,后面会解释。
buildSrc项目
1、创建好项目之后,新建一个名称为buildSrc
的Module,项目类型任意,只保留build.gradle
文件和src/main
目录,其余文件全部删掉。注意:名字一定要是buildSrc
,否则应用插件的时候会找不到插件。修改后的目录如下:
踩过的坑:创建buildSrc
这个Module的时候,如果选择了Android Library
类型会有Plugin with id 'com.android.library' not found.
的异常,这是因为buildSrc是Android的保留名称,只能作为plugin插件使用,后面修改buildSrc的build.gradle
文件后就不报错了。如果选择Java Library
类型,好像就没有这个异常,而且这个类型的文件少一些,为了方便,建议大家选择Java Library
类型。
2、修改Gradle文件内容:
apply plugin: 'groovy' //必须
apply plugin: 'maven'
dependencies {
implementation gradleApi() //必须
implementation localGroovy() //必须
//如果要使用android的API,需要引用这个,实现Transform的时候会用到
//implementation 'com.android.tools.build:gradle:3.3.0'
}
repositories {
//google()
jcenter()
mavenCentral() //必须
}
注意:如果引入了com.android.tools.build:gradle:3.3.0
,需要加入google()
仓库,我试过只引入 jcenter()
和 mavenCentral()
仓库中,会提示找不到。
3、在main
下新建groovy
目录,在groovy
目录下创建包名
目录,在包名
目录下新建一个groovy
文件,并且实现org.gradle.api.Plugin
接口,注意文件名需要以.groovy
结尾。
4、在main
下新建resources
目录,在resources
目录下新建META-INF
目录,再在META-INF
下新建gradle-plugins
目录,在gradle-plugins
目录下新建properties
文件,比如com.zx.plugin.properties
,注意:这个文件命名是没有要求的,但是要以.properties
结尾。
buildSrc项目目录如下:
项目结构CusPlugin.groovy
源码如下:
注意:这里是groovy语言写的,当然也可以用java、kotlin写,他们都是基于JVM的。Plugin
和Project
是Gradle的API,所以需要先在脚本文件中配置好了再写插件实现类,否则是找不到这2个类的。
package com.zx.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
class CusPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("this is CusPlugin")
}
}
这里没有去定义一系列Task任务,只是简单的打印Log。在实际应用中,会定义一些任务去执行,这个后面的文章会讲。
META-INF/gradle-plugins/com.zx.plugin.properties
文件的内容如下:
implementation-class=com.zx.plugin.CusPlugin
它的作用是:申明Gradle插件的具体实现类
5、在要使用插件的Module中应用,比如在app
的build.gradle
中,引用插件如下:
apply plugin: 'com.android.application'
//引用自定义插件
apply plugin: 'com.zx.plugin'
注意这里引用的插件名称就是properties
文件的名称。接下来如果编译正常的话,就会看见插件实现类的apply()
方法中打印的日志。
小结:
- module名称只能为
buildSrc
; - buildSrc project下的插件是自动加载。
独立的项目使用
这种方案的Module名称可以自定义,可以发布到本地或者远程仓库(jcenter、maven等)中,这样就可以供其他项目使用。
1、新建一个Module,项目类型任意,名字任意,也是只保留build.gradle
文件和src/main
目录,其余文件全部删掉。
2、修改Gradle文件内容:
apply plugin: 'groovy' //必须
apply plugin: 'maven' //要想发布到Maven,此插件必须使用
dependencies {
implementation gradleApi() //必须
implementation localGroovy() //必须
}
repositories {
mavenCentral() //必须
}
def group='com.zx.cus_plugin' //组
def version='1.0.0' //版本
def artifactId='myGradlePlugin' //唯一标示
//将插件打包上传到本地maven仓库
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = group
pom.artifactId = artifactId
pom.version = version
//指定本地maven的路径,在项目根目录下
repository(url: uri('../repos'))
}
}
}
相比buildSrc
方案,增加了Maven
的支持和uploadArchives
这样一个Task,这个Task的作用是为了将插件打包上传到本地maven仓库。注意打包文件目录是../repos
,它表示的是项目根目录下,这里用了2个.
,1个.
表示当前module根目录,2个.
表示project的根目录。
3、src/main
目录下的插件实现类和properties
文件与buildSrc
方案是一致的。
4、在终端中执行gradle uploadArchives指令,或者展开AS右侧的Gradle,找到对应moduleuploadArchives
Task,就可以将插件部署到项目根目录的repos
目录下。
插件部署到本地后的目录如下:
这个repos
就是你本地的Maven仓库,com/zx/cus_plugin
是脚本中的group
指定的,myGradlePlugin
表示模块名称,是一个唯一标示,1.0.0
由version
指定
5、引用插件
在buildSrc
中,系统自动帮开发者自定义的插件提供了引用支持,但完全自定义Module的插件中,开发者就需要自己来添加自定义插件的引用支持。
在project的build.gradle
文件中,添加如下脚本:
buildscript {
repositories {
google()
jcenter()
maven {
url uri('./repos') //指定本地maven的路径,在项目根目录下
}
}
dependencies {
//classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.zx.cus_plugin:myGradlePlugin:1.0.0'
}
}
注意:
-
这里的uri只用了1个
.
,因为project的build.gradle
文件已经在项目根目录下了 -
classpath
指定的路径格式如下:
这3个参数是在build.gradle
脚本文件中申明的
classpath '[groupId]:[artifactId]:[version]'
配置完毕后,在需要使用的Module引用插件,例如:
apply plugin: 'com.android.application'
//引用buildSrc插件,properties文件名称的方式
//apply plugin: 'com.zx.plugin'
//引用完全自定义插件,properties文件名称的方式
apply plugin: 'com.zx.cus_plugin'
小结:
-
方案3用完全自定义Module实现自定义插件,这里是打包上传插件到本地Maven,当然你也可以发布到远程仓库,参考:gradle插件上传Jcenter与自建Maven私服
-
上传本地Maven需要注意脚本文件中
uploadArchives
配置,以及引用插件时地址的配置(我在这里踩过坑),一般放在project根目录下就可以 -
如果重新修改了插件 代码,需要重新部署
uploadArchives
才能在别的地方引用插件
总结
虽然官方提供了3种方案,但实际开发中只会用到后2种,有一个比较方便的做法,开发调试的时候用buildSrc
project模式,等后面需要把插件共享出去等时候,再把它改为第三种方案即可。