暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件

一、前言


 Gradle 模块分为如下四部分:

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第1张图片

 其中 Setting 类在之前的篇幅中已经学习过了,Android 对 gradle 的扩展我们放到下一篇来学习。这篇我们先来学习 SourceSet 以及自定义 Plugin 插件。

 

二、SourceSet


SourceSet 主要是用来设置我们项目中源码或资源的位置的,目前它最常见的两个使用案例就是如下两类:

  • 修改 so 库存放位置。

  • 资源文件分包存放。

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第2张图片

此外,我们也可以使用如下代码将 sourceSets 在 android 闭包的外部进行定义:

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第3张图片

 

三、Gradle Plugin(插件)


自定义 Gradle 插件的本质就是把逻辑独立的代码进行抽取和封装,以便于我们更高效地通过插件依赖这一方式进行功能复用。而在 Android 下的 gradle 插件共分为两大类,如下所示:

  1. 脚本插件:同普通的 gradle 脚本编写形式一样,通过 apply from: 'test.gradle' 引用。
  2. 对象插件:通过插件全路径类名或 id 引用。

 

3.1 脚本插件

脚本插件就是一个普通的 gradle 构建脚本,我们既可以写在 build.gradle 里面,也可以自己新建一个 gradle 脚本文件进行编写。如果是新建一个 gradle 脚本,则需要通过 apply from 引用。

//test.gradle
project.task("showConfig") {
    doLast {
        println("$project.name:showConfig")
    }
}

//build.gradle
apply from: '../test.gradle'

 

3.2 对象插件

对象插件是指实现了 org.gradle.api.Plugin 接口的类。Plugin 接口需要实现 void apply(T target) 这个方法。该方法中的泛型指的是此 Plugin 可以应用到的对象,而我们通常是将其应用到 Project 对象上。编写对象插件主要有三种方式:

  • 直接在gradle脚本文件中
  • 在buildSrc目录下
  • 在独立的项目下

3.2.1 在gradle脚本文件中

直接在 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

3.2.2 在buildSrc目录下

除了直接写在某个模块的构建脚本中,我们还可以将插件写在工程根目录下的 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'

目录结构如下:

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第4张图片

3.2.3 在独立工程下

在 buildSrc 下创建的 plugin 只能在该工程下的多个模块之间复用代码。如果想要在多个项目之间复用这个插件,我们就需要在一个单独的工程中编写插件,将编译后的 jar 包上传到 maven 仓库。这里为了不增加复杂度,我们还是在该工程下创建一个 standaloneplugin 模块(java library module)。只需要明白我们完全可以在一个独立的工程下来编写插件。我们看下创建好后的目录结构:

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第5张图片

从目录结构来看,和 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'

 

3.3 对象插件中自定义 Extension 与 Task

3.3.1 自定义 Extension

下面我们来看下自定义 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 :

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第6张图片

可以看到正常打印了我们配置的 releaseInfo。

3.3.2 自定义 Task

使用自定义扩展属性 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:

暴力突破 Gradle 自动化项目构建(七)- 其他模块及自定义 Gradle 插件_第7张图片

你可能感兴趣的:(Gradle)