作为Android开发者,你可能见过无数个apply plugin:plugin_name
,plugin_name
对应着相应的插件。
例如:
apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
com.android.application
就对应着可以一个构建APK的Gradle插件,而com.android.library
则对应着一个构建android library
的插件。
Gradle插件开发支持Java、Groovy、Scala
三种语言开发,Groovy
用于实现与 Gradle 构建生命周期(如 task 的依赖)有关的逻辑,Java
用于核心逻辑,表现为 Groovy 调用 Java 的代码。
插件的打包方式
Gradle的插件有三种打包方式,主要是按照复杂程度和可见性来划分:
类型 | 说明 |
---|---|
Build script | 把插件写在 build.gradle 文件中,一般用于简单的逻辑,只在该 build.gradle 文件中可见 |
buildSrc 项目 | 将插件源代码放在 rootProjectDir/buildSrc/src/main/groovy 中,只对该项目中可见,适用于逻辑较为复杂,但又不需要外部可见的插件,可以参见img-optimizer-gradle-plugin |
独立项目 | 一个独立的 Groovy 和 Java 项目,可以把这个项目打包成 Jar 文件包,一个 Jar 文件包还可以包含多个插件入口,将文件包发布到托管平台上,供其他人使用。本文将着重介绍此类。 |
接下来我将从Build script和独立项目详细介绍。
Buid script
把插件写在build.gradle
文件中,这种是最简单的插件开发方式。
下边举个例子:
build.gradle//文件名
class GreetingPluginExtension{
String message = "Hello from GreetingPlugin"
}
class GreetingPlugin implements Plugin<Project>{
@Override
void apply(Project project){
def extension = project.extensions.create('greeting',GreetingPluginExtension)
project.task('hello'){
doLast {
println extension.message
}
}
}
}
apply plugin: GreetingPlugin //应用插件,这样插件才能在gradle构建过程中生效。
在build.gradle
同级目录中,在命令行执行gradle greeting
。则会输出以下内容:
Hello from GreetingPlugin
这个插件创建了一个名为hello
的task,并在task中打印了GreetingPluginExtension#message
的内容。
create的第一个参数greeting
是我们自定义配置的DSL名字,第二个参数是参数类的名字。
通过greeting
这个DSL这个名字,我们可以任意的改变参数类中相应字段的值。这样就带来了很大的便利。
比如我们将Android工程的app目录中经常见到build.gradle
中会有
android {
compileSdkVersion 24
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "com.chenenyu.plugintest"
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
而这些就是名为android DSL的动态参数配置。
同样,我们把刚才写的build.gradle
中添加新的内容,如下:
class GreetingPluginExtension{
String message = "Hello from GreetingPlugin"
}
class GreetingPlugin implements Plugin<Project>{
@Override
void apply(Project project){
def extension = project.extensions.create('greeting',GreetingPluginExtension)
project.task('hello'){
doLast {
println extension.message
}
}
}
}
apply plugin:GreetingPlugin
greeting{
message="I am dynamic message"
}
重新执行gradle hello
,则会看到以下内容:
I am dynamic message
在本小节介绍了Gradle插件的基本开发,通过继承org.gradle.api.Plugin
即可简单快速的实现。同时我们通过project.extensions.create
可以实现动态传参。只需要定义DSL名字和定义相应的Class,并且在DSL作用域中重新赋值。
在Gradle插件开发中,所有的插件都要继承
org.gradle.api.Plugin
接口,并且需要重写void apply(Project project) 方法,这个方法将会传入使用这个插件的 project 的实例,这是一个重要的 context。
独立项目插件
独立的项目可以发布到本地或者jcenter仓库中,这样就会很方便第三方集成。
通常我们可以在build.gradle
中来改变APK最终输出的名字。我们换个套路,把这个功能放在插件中来完成。同时将该插件发布到本地仓库,同时在第三方APP中应用该插件。
001 创建项目
在 Android Studio 中新建 Java Library module “plugin”。
002 修改 build.gradle 文件
apply plugin: 'groovy'
apply plugin: 'maven'
repositories {
mavenLocal()
jcenter()
}
dependencies {
compile gradleApi()
}
//publish to local directory
def versionName = "1.0.0"
group "com.demon.plugin"
version versionName
uploadArchives{ //当前项目可以发布到本地文件夹中
repositories {
mavenDeployer {
repository(url: uri('./repo')) //定义本地maven仓库的地址
}
}
}
003 修改项目文件夹
src/main 项目文件下:
- 移除 java 文件夹,因为在这个项目中用不到 java 代码
- 添加 groovy 文件夹,主要的代码文件放在这里
- 添加 resources 文件夹,存放用于标识 gradle 插件的 meta-data
004 建立对应文件
├── build.gradle
└── src
└── main
├── groovy
│ └── com
│ └── demon
│ └── plugin
│ ├── ApkChangeNamePlugin.groovy
│
└── resources
└── META-INF
└── gradle-plugins
└── apk_change_name.properties
ApkChangeNamePlugin.groovy文件内容如下:
package com.demon.plugin
import org.gradle.api.Project
import org.gradle.api.Plugin
class ApkChangeNamePlugin implements Plugin<Project>{
@Override
void apply(Project project) {
if(!project.android){
throw new IllegalStateException('Must apply \'com.android.application\' or \'com.android.library\' first!');
}
project.android.applicationVariants.all{
variant ->
variant.outputs.all{
outputFileName = "${variant.name}-${variant.versionName}.apk"
}
}
}
}
apk_change_name.properties
文件内容如下:
implementation-class=com.demon.plugin.ApkChangeNamePlugin //指定实现类
注意:
- groovy文件夹中的类,一定要修改成 .groovy 后缀,IDE才会正常识别。
- resources/META-INF/gradle-plugins 这个文件夹结构是强制要求的,否则不能识别成插件。
-
apk_change_name.properties
中apk_change_name
为插件名
005 发布插件到本地目录中
执行如下命令即可
gradle uploadArchives
则会在build.gradle
的同级目中生成repo目录,里边的内容如下图:
006 在第三方APP中使用插件
在项目的 buildscript 添加插件作为 classpath
buildscript {
repositories {
maven{
url '/Users/demon.li/workspace/GradlePlugin/repo/'
}
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "com.demon.plugin:plugin:1.0.0"
}
}
在app module
中使用插件:
apply plugin: 'apk-change-name'
在最终的apk构建后会根据variant名字和版本的不同而输出不同名称的apk。
例如最终生成:
debug-1.0.apk
release-1.0.apk
总结
Gradle plugin开发是一个相对简单的过程。关键是看我们如何实现一些有意义的插件,比如img-optimizer-gradle-plugin就实现了在Android项目中压缩.png
图片的插件。
我们在这里也用到了Groovy
,如果想深入Gradle插件开发的同学可以学习一下Groovy语言的精妙。
Groovy也是借助于Java虚拟机运行的,Java虚拟机规范定义了字节码的规范。比如kotlin和Scala都是运行在Java虚拟机中的。
Reference: