Gradle插件开发指南

作为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.propertiesapk_change_name为插件名

005 发布插件到本地目录中

执行如下命令即可
gradle uploadArchives

则会在build.gradle的同级目中生成repo目录,里边的内容如下图:

Gradle Plugin

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:

  1. Writing Custom Plugins
  2. 拥抱 Android Studio 之五:Gradle 插件开发
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容