Gradle 学习之插件

本文内容大部分来自 Gradle 官方文档,英文 OK 的同窗能够直接看官方文档。 本文示例代码已放在 zjxstar 的 GitHubhtml

前言

提及 Gradle 插件,不得不感叹 Gradle 的设计很是棒。 Gradle 自己提供基本的概念以及核心框架,而其余的场景逻辑均可以经过插件扩展的方式来实现。对于 Android 开发来讲,经常使用的插件就是 Android Gradle 插件和 Java 插件了,咱们会在下一篇文章中详细介绍 Android 插件。java

本文主要学习 Gradle 插件的基础知识以及如何编写自定义插件。android

插件的做用

Gradle 插件一般用来封装一些可重用的编译逻辑,能够用来扩展项目的功能,帮助项目在构建过程当中处理一些特殊逻辑。git

  1. 能够给项目中添加自定义任务,好比测试、编译、打包等。
  2. 能够给项目添加依赖配置,好比第三方库等。
  3. 能够向项目中的对象类型添加新的扩展属性、方法等,从而帮助咱们配置、优化构建,好比 Android 插件的 android{ } 配置块。
  4. 能够对项目进行一些约定,好比约定 src/main/java 目录下存放源代码。

总之,咱们只要根据插件约定的接口,使用它提供的任务、方法或者扩展,就能够帮助咱们进行项目构建。github

如何应用一个插件

Gradle 中的插件种类分为两种,一种是脚本插件,另外一种是二进制插件。而应用插件的方式也有多种,下面咱们详细介绍。api

脚本插件

所谓的脚本插件就是咱们自定义的以 .gradle 为后缀的脚本文件,严格意义上来讲,它只是一个可执行脚本。应用它就是把整个脚本加载到主 build.gradle 脚本中,从而辅助构建。应用方式以下:网络

apply from: '脚本文件名' // 好比:apply from: 'project_task_examples.gradle'
复制代码

它不只能够应用本地脚本,也能够应用网络上的脚本,这样的话,就必须使用 HTTP URL 了。闭包

举例说明:app

在前两章的 Project 和 Task 的学习中,咱们在 app 模块的 build.gradle 文件中写了不少示例,咱们把这些示例都提到一个单独的 gradle 脚本中,而后在 build.gradle 中应用它。新的脚本文件命名: project_task_examples.gradle ,只须要使用 apply from: 'project_task_examples.gradle' 应用便可。框架

project_task_examples.gradle

虽然它不算真正的插件,但也不能忽视它的做用,它是脚本文件模块化的基础。咱们能够把庞大的脚本文件进行分块、分段整理,拆分红一个个共用的、职责分明的文件,而后使用 apply from 应用它们。这样可让咱们的脚本更加清晰、简单、方便和快捷。

二进制插件

二进制插件是实现了 org.gradle.api.Plugin 接口的插件(通常都是打包在 jar 里独立发布),它们拥有 plugin id ,这个 id 是插件全局惟一的标识或名称。咱们须要经过

apply plugin: 'plugin id'
复制代码

的方式应用二进制插件。好比 Android 的 Application 插件,咱们经过 Android Studio 建立 Android 工程时,在 app 模块中会自动经过 apply plugin: 'com.android.application' 来引入 Application 插件。

对于 Gradle 自带的核心插件都有一个容易记的短名,好比 Java 插件,经过 apply plugin:'java' 应用。其实它对应的类型是 org.gradle.api.plugins.JavaPlugin ,因此还能够经过该类型进行应用:apply plugin:org.gradle.api.plugins.JavaPlugin 。因为包 org.gradle.api.plugins 是默认导入的,因此咱们能够去掉包名直接写为: apply plugin:JavaPlugin 。

其实 apply 方法有重载方法:

void apply(Closure var1);

void apply(Action<? super ObjectConfigurationAction> var1);

void apply(Map<String, ?> var1);
复制代码

以前使用的是传入 Map 参数的方式,咱们也能够换成闭包的方式。

apply {
    plugin 'java'
}
复制代码

使用 plugins DSL 应用插件

plugins DSL 是一种新的插件应用方式,Gradle 2.1 以上版本才可使用。它的语法以下:

plugins {
    id «plugin id»                                            // (1)
    id «plugin id» version «plugin version» [apply «false»]   // (2)
}
复制代码

方式(1)是提供给 Gradle 核心插件或者构建脚本中已有的插件的。方式(2)提供给须要解析的二进制插件,这些插件要托管在 plugins.gradle.org/ 网站上。

示例:

plugins {
    id 'java'
}

plugins {
    id 'com.jfrog.bintray' version '0.4.1'
}
复制代码

固然,这种使用方式有必定的限制:

  1. 《plugin id》和《plugin version》必须是常量字符串。
  2. plugins { } 块必须是 build 脚本中的顶级语句。它不能嵌套在另外一个构造中。
  3. plugins { } 块目前只能在项目的构建脚本中使用,不能用于脚本插件、settings.gradle 文件或者 init 脚本。

插件的类型和应用方式就介绍到这,下面咱们要学习如何自定义插件,来知足本身的业务逻辑。

自定义插件

自定义插件的关键点就是要实现 org.gradle.api.Plugin 接口,重写 apply 方法来实现本身的业务逻辑。本讲中使用的示例都是构建 Task 的简单示例,感兴趣的同窗的能够自行深刻。

这里有三种方式来承载自定义插件的代码:构建脚本、buildSrc 项目、独立项目。

构建脚本

咱们能够直接在构建脚本中编写自定义插件的代码。 这样作的好处是插件能够自动编译并包含在构建脚本的类路径中,而无需执行任何操做。 可是,这样定义的插件在构建脚本以外是不可见的,即没法在其定义的构建脚本以外重用该插件。

示例:

/* 自定义插件:直接在脚本文件中定义,其局限性较大 */
// app的build.gradle文件中
// 定义一个Extension类用来接收脚本传的参数
class CustomExtensionA {
    String message = 'Hello Custom PluginA'

    // 还能够定义其余配置参数
    String greeter = 'Welcome Gradle Plugin'
}

// 定义一个自定义插件类,实现Plugin接口
class CustomPluginA implements Plugin<Project> {

    @Override
    void apply(Project target) {

        // 将Extension注册给Plugin
        def extension = target.extensions.create('customA', CustomExtensionA)

        // 定义一个任务
        target.task('CustomPluginTaskA') {
            doFirst {
                println 'this is custom plugin A task A'
            }

            doLast {
                // 使用Extension传入的参数
                println extension.message
                println extension.greeter
            }
        }
    }
}
复制代码

例子中,咱们建立一个 CustomPluginA 类,实现 Plugin 接口,重写 apply 方法。在 apply 方法中使用 project 建立一个名为 CustomPluginTaskA 的任务。该插件还能够接受扩展,即定义了一个 CustomExtensionA 类,里面有 message 和 greeter 两个属性。在 apply 方法中,经过 project.extensions.create(扩展名, 扩展类) 就能够完成自定义扩展的注册。

应用该插件只须要在 build.gradle 文件中使用 apply plugin 引入便可。

// 使用插件CustomPluginA,这里必须使用插件类名,而不是字符串
// 使用gradlew -q CustomPluginTaskA查看插件是否生效
apply plugin: CustomPluginA

// 配置CustomExtensionA,须要使用注册名,这里是customA
customA.message = 'Hi from PluginA'
复制代码

引入插件后,能够像运行普通任务同样经过命令运行插件中定义的 Task 。至于扩展的使用,只需使用扩展名声明对应属性便可。

buildSrc 项目

咱们能够在工程里新建一个名为 buildSrc 的模块来开发自定义插件,其过程和写 Android Library 相似。buildSrc 模块中的代码会自动被 Gradle 加载,Gradle 将负责编译和测试插件并使其在构建脚本的类路径中可用。 这种方式定义的插件对于构建中的每一个构建脚本都是可见的。 可是,它在构建以外是不可见的,所以没法在其定义的构建以外重用该插件。

新建 buildSrc 模块:

buildSrc自定义插件

该模块很是简单,只须要有 src/main/groovy 目录用来存放源代码便可(由于使用的 Groovy 语言编写,因此是 groovy,若是使用 java 或者 kotlin ,换成对应目录便可)。

在 build.gradle 脚本中依赖 gradleApi 和 Groovy 插件。

// 依赖groovy插件
plugins {
    id 'groovy'
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}
复制代码

接着就是写代码了,这个过程和“构建脚本”小节中同样,只是将代码放在了 groovy 文件中而已。

// 在单独的groovy文件中定义插件类
class CustomPluginB implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task('CustomPluginTaskB') {
            doFirst {
                println 'This is custom plugin TaskB'
            }
        }
    }
}
复制代码

最后,就是在 app 模块中引入该插件。

/* 自定义插件:引用在buildSrc模块中定义的插件,使用插件类名的全限定名 */
// 使用gradlew -q CustomPluginTaskB查看插件是否生效
apply plugin: com.zjx.happy.plugin.CustomPluginB
复制代码

因为构建过程当中 Gradle 会自动加载 buildSrc 模块中的代码,因此直接使用插件类的全限定名便可。

独立项目

咱们能够为插件建立单独的项目。将该项目发布成一个 JAR ,就能够在多个地方使用它。这种方式是比较经常使用的。

首先,和 buildSrc 项目同样建立一个新模块,模块名没有要求。

独立项目自定义插件

和 buildSrc 不一样的是,它多出了一个 resources 资源目录。一样的,这个模块里也要依赖 gradleApi 和 Groovy 插件。

而后,在 groovy 目录下完成代码的编写。这里依然只是写个简单示例。

class CustomPluginC implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task('CustomPluginTaskC') {
            doFirst {
                println 'This is Custom Plugin TaskC'
            }
        }
    }
}
复制代码

至此,插件的核心已经准备好了,如今要给这个插件设置一个 plugin id 。须要在 resources 目录下,建立 META-INF 目录。而后在 META-INF 目录下再建立一个 gradle-plugins 目录。( 注意: 这里建立目录时请逐级建立。不要经过一次性输入 META-INF.gradle-plugins 的目录名方式建立,由于 resources 目录没法像代码目录那样自动识别包名并分级,它只会建立一个目录,这样是不正确的。)

目录建立好后,咱们须要在该目录下,新建一个 properties 文件,而文件名就是插件的 plugin id 。该 properties 文件中须要声明插件的实现类,语法以下:

# 建立该目录时必定要注意,要一级一级的建立,先建立META-INF,再建立gradle-plugins,而不是直接META-INF.gradle-plugins
# 由于不是src的目录,因此没法自动识别 . 号
# implementation-class是固定的,等号右边是插件实现类的全限定类名
implementation-class=com.zjx.happy.plugin.CustomPluginC
复制代码

详细目录结构如图:

resources目录

本示例中的插件的 plugin id 就是 com.happy.custompluginc 。那么 plugin id 有哪些规范呢?

  • 能够包含任何字母、数字、.-
  • 必须至少包含一个 . 将命名空间和插件名分开
  • 一般使用小写反向域名来约定命名空间
  • 插件名仅使用小写字符
  • 命名空间 org.gradle 和 com.gradleware 不可使用;
  • 不能以 . 开头和结尾
  • 不能使用连续的 . ,好比: ..

插件准备好后,咱们要将其发布出去,通常使用 Maven 。

// build.gradle中
apply plugin: 'maven' // 引入 maven 插件

repositories {
    mavenCentral()
}

// 将该插件上传到本地Maven库
group='com.happy.plugin'
version='1.0.0'

// 经过gradlew :customPlugin:uploadArchives命令上传
uploadArchives {
    repositories {
        mavenDeployer {
            //本地的Maven地址设置为../repos
            repository(url: uri('../repos'))
        }
    }
}
复制代码

使用 gradlew :customPlugin:uploadArchives 命令上传。示例中会在工程目录下建立 repos 目录,里面存放了 customPlugin 的 jar 包和 pom 信息。

repos目录

如何应用独立模块自定义的插件呢?其方法和 Android 插件同样。

在根 build.gradle 文件中声明插件依赖:

dependencies {
    	// 这是Android插件
        classpath 'com.android.tools.build:gradle:3.3.1'
		// 这是刚自定义的插件
        classpath 'com.happy.plugin:customPlugin:1.0.0'
    }
复制代码

同步以后,就能够在 app 模块中使用 apply plugin 引入了。

/* 自定义插件:引用已经发布到Maven的插件 */
apply plugin: 'com.happy.custompluginc' // 这个plugin id就是properties文件的文件名
复制代码

最后,使用命令 gradlew -q CustomPluginTaskC 验证插件是否生效。

总结

Gradle 插件相关的内容就这么多,总体上看仍是很简单的。如今社区中的第三方插件不少,咱们能够根据业务须要适当引用。固然,咱们也彻底能够本身实现适合业务的插件。通常 APM 类项目中 中就须要实现自定义插件来完成代码的自动插桩,它须要利用到 Android 提供的 Gradle-api 中的 Transform 。后续的文章中咱们会学习 Transform 。

参考资料

  1. Gradle 官方文档-使用插件
  2. Gradle 官方文档-自定义插件
  3. 《Android Gradle权威指南》
  4. 《Gradle for Android 中文版》
相关文章
相关标签/搜索