Gradle 模块分为如下四部分:
其中 Setting 类在之前的篇幅中已经学习过了,Android 对 gradle 的扩展我们放到下一篇来学习。这篇我们先来学习 SourceSet 以及自定义 Plugin 插件。
SourceSet 主要是用来设置我们项目中源码或资源的位置的,目前它最常见的两个使用案例就是如下两类:
修改 so 库存放位置。
资源文件分包存放。
此外,我们也可以使用如下代码将 sourceSets 在 android 闭包的外部进行定义:
自定义 Gradle 插件的本质就是把逻辑独立的代码进行抽取和封装,以便于我们更高效地通过插件依赖这一方式进行功能复用。而在 Android 下的 gradle 插件共分为两大类,如下所示:
脚本插件就是一个普通的 gradle 构建脚本,我们既可以写在 build.gradle 里面,也可以自己新建一个 gradle 脚本文件进行编写。如果是新建一个 gradle 脚本,则需要通过 apply from 引用。
//test.gradle
project.task("showConfig") {
doLast {
println("$project.name:showConfig")
}
}
//build.gradle
apply from: '../test.gradle'
对象插件是指实现了 org.gradle.api.Plugin 接口的类。Plugin 接口需要实现 void apply(T target) 这个方法。该方法中的泛型指的是此 Plugin 可以应用到的对象,而我们通常是将其应用到 Project 对象上。编写对象插件主要有三种方式:
直接在 gradle 脚本中编写这个方式是最为简单的。打开 app.gradle 文件,在其中编写一个类实现 Plugin 接口:
//app.gradle
class CustomPluginInBuildGradle implements Plugin {
@Override
void apply(Project target) {
target.task('showCustomPluginInBuildGradle'){
doLast {
println("task in CustomPluginInBuildGradle")
}
}
}
}
然后通过插件类名引用它:
//app.gradle
apply plugin: CustomPluginInBuildGradle
除了直接写在某个模块的构建脚本中,我们还可以将插件写在工程根目录下的 buildSrc 目录下,这样可以在多个模块之间复用该插件。虽然 buildSrc 是 Gradle 在项目中配置自定义插件的默认目录,但它并不是标准的 Android 工程目录,所以使用这种方式需要我们事先手动新建一个 java library module,该 module 必须命名为 buildSrc。将 src/main/java 改成 src/main/groovy。
在 buildSrc 目录下新建一个 build.gradle,并在 build.gradle 中引用 groovy 插件:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
然后新建一个 xxxPlugin.groovy 并实现 Plugin 接口,例如:
package com.lerendan.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin {
@Override
void apply(Project project) {
project.task('pluginTest') {
doLast {
println 'Hello World'
}
}
}
}
可以看到,上述 plugin 仅是在 apply() 方法内部创建了一个名为 pluginTest 的 task。由于 buildSrc 目录是 gradle 默认的目录之一,该目录下的代码会在构建是自动编译打包,并被添加到 buildScript 中的 classpath 下,所以不需要任何额外的配置,就可以直接被其他模块的构建脚本所引用。
通过类名引用插件的需要使用全限定名,也就是需要带上包名,或者可以先导入这个插件类,如下:
//app.gradle
apply plugin: com.lerendan.buildsrc.TestPlugin
注意这里引用的方式可以是通过类名引用,也可以通过给插件映射一个 id,然后通过 id 引用。通过简单的 id 的方式,我们可以隐藏类名等细节,使的引用更加容易。映射方式很简单,在 buildSrc目录下创建 resources/META-INF/gradle-plugins/xxx.properties,这里的 xxx 也就是所映射的 id,这里我们假设取名myplugin。myplugin.properties 文件中配置该 id 所对应的 plugin 实现类:
implementation-class=com.lerendan.buildsrc.TestPlugin
此时就可以通过 id 来引用对象的插件了:
//app.gradle
apply plugin: 'myplugin'
目录结构如下:
在 buildSrc 下创建的 plugin 只能在该工程下的多个模块之间复用代码。如果想要在多个项目之间复用这个插件,我们就需要在一个单独的工程中编写插件,将编译后的 jar 包上传到 maven 仓库。这里为了不增加复杂度,我们还是在该工程下创建一个 standaloneplugin 模块(java library module)。只需要明白我们完全可以在一个独立的工程下来编写插件。我们看下创建好后的目录结构:
从目录结构来看,和 buildSrc 目录是一致的。区别在于 buildSrc 下的代码在构建时会自动编译并被引用。而我们在独立项目中编写的插件如果要能正确的被引用到,需要上传到 maven 仓库中,然后显式地在需要引用的项目中的 buildSrcipt 中添加对该构件的依赖。
接下来我们来完成插件代码:
package com.lerendan.aloneplugin
import org.gradle.api.Plugin
import org.gradle.api.Project
class AlonePlugin implements Plugin {
@Override
void apply(Project project) {
project.task('showAlonePlugin') {
doLast {
println('task in AlonePlugin')
}
}
}
}
在 alonePlugin 目录下新建一个 build.gradle,并在 build.gradle 中引用插件项目构建脚本:
//alonePlugin build.gradle
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
group = 'com.lerendan.aloneplugin'
version = '1.0.0'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
这里与 buildSrc 不同的是,我们引用了 apply plugin 'maven',通过 maven 插件,我们可以轻松的配置 group,version 以及 uploadArchives 的相关属性,然后执行 gradlew uploadArchives 这个任务,就可以将构件打包后上传到 maven 仓库了。同样为了示例简单,我们上传到一个本地仓库 repository(url: uri('../repo')) 中。
上传之后就可以在项目根目录下找到 repo 这个目录了。最后我们通过给根目录下的 build.gradle 配置 buildScript 的 classpath,就可以引用这个插件了。注意,classpath 的格式为 group:artifact:version。
//根目录下 build.gradle
buildscript {
repositories {
maven {
url uri('repo')
}
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.lerendan.aloneplugin:alonePlugin:1.0.0'
}
}
引用插件:
//app.gradle
apply plugin: 'testAlonePlugin'
下面我们来看下自定义 Extension 的步骤,首先创建一个实体类:
//ReleaseInfoExtension.groovy
package com.lerendan.buildsrc
class ReleaseInfoExtension{
String versionName
String versionCode
String versionInfo
@Override
String toString() {
return "versionName = $versionName , versionCode = $versionCode , versionInfo = $versionInfo"
}
}
接着我们在 buildSrc 中创建一个 Gradle 插件:
//TestPlugin.groovy
package com.lerendan.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin {
@Override
void apply(Project project) {
project.extensions.create("releaseInfo", ReleaseInfoExtension.class)
project.task('pluginTest') {
doLast {
ReleaseInfoExtension releaseInfo = project.releaseInfo
println releaseInfo
}
}
}
}
可以看到,我们通过 project.extensions.create 创建了这样一个自定义的 Extension。接着我们在 app module 的 build.gradle 中引入这个 plugin,就可以在 app moudle 的 build.gradle 脚本中使用 releaseInfo 去配置扩展属性,代码如下所示:
//app module 的 build.gradle
apply plugin: com.lerendan.buildsrc.TestPlugin
releaseInfo {
versionCode = "1"
versionName = "1.0.0"
versionInfo = "第一个版本~"
}
......
执行这个 task :
可以看到正常打印了我们配置的 releaseInfo。
使用自定义扩展属性 Extension 仅仅是为了让使用插件者有配置插件的能力。而插件还得借助自定义 Task 来实现相应的功能,必须继承 DefaultTask。这里我们创建一个 Task,其具体实现代码如下所示:
//ReleaseInfoTask.groovy
package com.lerendan.buildsrc
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class ReleaseInfoTask extends DefaultTask {
ReleaseInfoTask() {
// 1、在构造器中配置了该 Task 对应的 Task group,即 Task 组,并为其添加上了对应的描述信息。
group = 'version_manager'
description = 'release info update'
}
// 2、在 gradle 执行阶段执行
@TaskAction
void doAction() {
updateVersionInfo()
}
private void updateVersionInfo() {
// 3、从 realeaseInfo Extension 属性中获取相应的版本信息
ReleaseInfoExtension releaseInfoExtension = project.extensions.releaseInfo
println releaseInfoExtension
}
}
首先我们在构造器中配置了该 Task 对应的 Task group,即 Task 组,并为其添加上了对应的描述信息。接着,我们使用了 @TaskAction 注解标注了 doAction 方法,这样这个方法就会在 gradle 执行阶段执行。最后可以使用 project.extensions.releaseInfo 从 realeaseInfo Extension 属性中了获取相应的配置信息。
可以看到,自定义的插件 task 都会遵循这个步骤,当然,最后别忘了在我们的 TestPlugin 的 apply 方法中加入下面代码去创建 ReleaseInfoTask 实例,代码如下所示:
//TestPlugin.groovy
package com.lerendan.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin {
@Override
void apply(Project project) {
// 创建extension
project.extensions.create("releaseInfo", ReleaseInfoExtension.class)
// 创建task
project.tasks.create("releaseInfoTask", ReleaseInfoTask.class)
}
}
执行 releaseInfoTask 这个 task: