Gradle进阶计划(三)自定义Gradle Plugin

        上一篇文章Gradle进阶计划(二)Gradle Plugin原理分析,我们介绍了 Gradle Plugin 的原理,并且对于主要的 Task 进行了详细的分析。这篇文章我们研究一下如何自己写一个Gradle Plugin。

 

一、自定义Task

        在学习自定义 Gradle Plugin 之前,我们先学习一下如何自定义 Task。

        相较于自定义 Gradle Plugin,自定义Task实现更加的简单。同时,它又是自定义 Gradle Plugin 的基础,我们自定义 Gradle Plugin 时一般都会去自定义 Task。对于一些简单的需求,我们可以直接自定义 Task 而不是 Gradle Plugin。

(一)什么是Task

        其实通过前面的学习,我们已经对 Task 很熟悉了,这里我们再来回顾一下。

        Task 是 Gradle 执行的基本单元,为什么这么说?实际上根据 Gradle 构建项目的流程,是先把所有的 ​.gradle​ 脚本执行一遍,编译生成对应的 Gradle、Setting、Project 对象,然后根据我们在构建脚本中设置的构建配置,生成一张 Task 组成的:​有向无环图​,先来看看这张图:

        

        Gradle中,每一个待编译的工程都叫一个Project。每一个Project在构建的时候都包含一系列的Task。比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等。一个Project到底包含多少个Task,其实是由编译脚本指定的插件决定。插件是什么呢?插件就是用来定义Task,并具体执行这些Task的东西。

        Task 我们可以看成一个个任务,这个任务可以是编译 java 代码、编译 C/C++ 代码、编译资源文件生成对应的 R 文件、打包生成 jar、aar 库文件、签名、混淆、图片压缩、打包生成 APK 文件、包/资源发布等。不同类型的项目 Task 任务种类不同,Gradle 现在可以构建 java、android、web、lib 项目。

        下图中这些都是一个个 Task 任务,每一个 Task 任务都是为了实现某个目的,可以理解为一个步骤,最终一个个步骤按序执行就完成了整个项目构建。

         

        Task 是完成一类任务,实际上对应的也是一个对象。 Task 是由无数个 Action 组成的Action 代表的是一个个函数、方法,每个 Task 都是一堆 Action 按序组成的执行图,就好像我们在 Class 的 main 函数中按照逻辑调用一系列方法一样。

(二)如何自定义Task

        Gradle 中 Task 都是继承自 DefaultTask,我们自定义 Task 也需要继承这个类,重点是写自己需要的方法,然后加上​ @TaskAction ​注解表示这个方法是 Task 中的 action,可以加多个,按照倒序执行。

class MyTask extends DefaultTask {

    String message = "mytask..."

    @TaskAction
    def ss1() {
        println("This is MyTask --> action1!")
    }

    @TaskAction
    def ss2() {
        println("This is MyTask --> action2!")
    }

}


task speak(type: MyTask) {
    println("This is AA!")
    doFirst {
        println("This is doFirst!")
    }
    doLast {
        println("This is doLast!")
    }
}

1. 创建Task

task hello{
    println "hello world"
}

task(hello2){
    println "hello world2"
}

task ('hello3'){
    println "hello world3"
}

2. 动态创建 Task

times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}

3. Task 额外属性

task asss{
    ext {
        name = "AA"
        age = 18
    }

    doLast {
        println("name = "+ name)
        println("age = "+ age)
    }
}

4. Task 的 action

        上面说过 Task 内部 action 也不是唯一的,而是一个集合,我们可以往里面添加各种 action,要注意 action 之间有前后执行顺序的,这个是规定好了的。这些 action 接收都是闭包,看下面 Task 中的声明。

 //在 Action 队列头部添加 Action
 Task doFirst(Action<? super Task> action);
 Task doFirst(Closure action);

 //在 Action 队列尾部添加 Action
 Task doLast(Action<? super Task> action);
 Task doLast(Closure action);

 //删除所有的 Action
 Task deleteAllActions();

        doFirst{...}、doLast{...} 接受的都是闭包:

  • doFirst 添加的 action 最先执行
  • task 自身的 action 在中间执行,这个无法在 task 外部添加,可以在自定义 task 时写
  • doLast 添加的 action 最后执行

        另外 task 也可以向对象那样操作,task 里面也可以写代码,比如打印 AA,但是这些代码只能在配置阶段执行,而 task 的 action 都是在运行阶段执行的。

task speak{
    println("This is AA!")
    doFirst {
        println("This is doFirst!")
    }
    doLast {
        println("This is doLast!")
    }
}

speak.doFirst {
        println("This is doFirst!")
 }

         

5. Task 之间共享数据

        两个 Task 之间使用、操作同一个数据,需要借助 ext 全局变量操行。

ext {
    name = "AAA"
}

def age = 18

task s1 {
    doLast {
        age = 12
        rootProject.ext.name = "BBB"
        println("This is s1...")
    }
}

task s2 {
    doLast {
        println("age --> " + age)
        println("name --> " + rootProject.ext.name)
        println("This is s2...")
    }
}

6. 系统默认 Task

        Gradle 默认提供了很多 task 给我们使用,比如 copy、delete等。

task speak (type: Copy) {
    ...
}

task copyImage(type: Copy) {
    from 'C:\\Users\\zyj\\Desktop\\gradle\\copy'
    into 'C:\\Users\\zyj\\Desktop'
    include "*.jpg"
    exclude "image1.jpg"
    rename("image2.jpg","123.jpg")
}

7. 设置默认 Task 任务

        设置这个的意思的是指,脚本中我们不调用该 task,设置的 task 也会执行。

defaultTasks 'clean', 'run'

task clean {
    doLast {
        println 'Default Cleaning!'
    }
}

task run {
    doLast {
        println 'Default Running!'
    }
}

task other {
    doLast {
        println "I'm not a default task!"
    }
}
> gradle -q

Default Cleaning!
Default Running!

8. Task 中使用外部依赖

        buildscript{...} 引入远程仓库和依赖 path,import 导入进来就可以了。

import org.apache.commons.codec.binary.Base64

buildscript {
        // 导入仓库
    repositories {
        mavenCentral()
    }
    // 添加具体依赖
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

task encode {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}

9. 运行 Task

        命令行执行 ​gradle XXX

(三)Task中的一些重要方法

  • Task 行为
    • Task.doFirst
    • Task.doLast
  • Task 依赖顺序
    • Task.dependsOn
    • Task.mustRunAfter
    • Task.shouldRunAfter
    • Task.finalizedBy
  • Task 的分组描述
    • Task.group
    • Task.description
  • Task 是否可用
    • Task.enabled
  • Task 输入输出
    • gradle 会比较 task 的 inputs 和 outputs 来决定 task 是否是最新的,如果 inputs 和 outputs 没有变化,则认为 task 是最新的,task 就会跳过不执行
    • Task.inputs
    • Task.outputs
  • Task 是否执行
    • 可以通过指定 Task.upToDateWhen = false 来强制 task 执行 Task.upToDateWhen

 

二、自定义Gradle Plugin

        gradle 的插件可以看作是一系列 task 的集合。在 android 工程的 build.gradle 脚本里,第一行就是 apply plugin: 'com.android.application',这个就是引入 android gradle 插件,插件里有 android 打包相关的 task。

(一)初始化工程

        1. 在 android studio 中创建一个 java module。注意,这里有两种方法,不同之处在于新建的工程名不同,步骤有所不同:

  • 方法1: 可以任意起名称接着走如下步骤;
  • 方法2: 工程名叫buildSrc,这样就可以跳过下面的“本地安装插件”和“插件发布”流程了,每次都会默认编译buildSrc项目。

        2. 在 src/main 目录下创建 groovy 目录,然后创建自己的包名和插件类。

        3. 在 src/main 目录下创建 resources/META-INFO/gradle-plugins 目录,创建 myplugin.properties 文件,文件里内容是: 

implementation-class=com.example.plugin.MyPlugin  //这里是自己的插件类,入口类

        4. 修改 build.gradle 文件:

// 引入 groovy 和 java 插件
apply plugin: 'groovy'
apply plugin: 'java'

buildscript {
    repositories {
        mavenLocal()
        maven { url 'https://maven.google.com' }
        jcenter()
    }
}

repositories {
    mavenLocal()
    maven {
        url 'http://depot.sankuai.com/nexus/content/groups/public/'
    }
    maven { url 'https://maven.google.com' }
}

dependencies {
    compile gradleApi()
    compile localGroovy()
    compile 'com.android.tools.build:gradle:3.0.1'  //版本
}

        现在为止,项目结构是这个样子的:

        

(二)创建Plugin 

        在刚才创建的插件类里,就可以写插件的代码了。插件类继承 Plugin,并实现 apply 接口,apply 就是在 build.gradle 里 apply plugin 'xxx' 的时候要调用的接口了。插件开发可以使用 groovy 和 java,使用 groovy 的话可以有更多的语法糖,开发起来更方便一些。

package com.example.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        println("apply my plugin")
    }
}

(三)创建插件的Task

        1. 我们再定义一个 task 类 MyTask,继承自 DefaultTask,简单的输出一些信息。

package com.example.plugin

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class MyTask extends DefaultTask {

    @TaskAction
    void action() {
        println('my task run')
    }
}

        2. 然后在 plugin 中注册这个 task。

class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        println("apply my plugin")
        project.tasks.create("mytask", MyTask.class)
    }
}

        这样一个简单的插件就开发好了,如何使用呢?

(四)本地安装插件

        1. 我们首先需要在 build.gradle 中引入 maven 插件,并且配置 install 相关的属性。

apply plugin: 'maven'

install {
    repositories.mavenInstaller {
        pom.version = '0.0.1' // 配置插件版本号
        pom.artifactId = 'myplugin' // 配置插件标识
        pom.groupId = 'com.example.plugin' // 配置插件组织
    }
}

        2. 之后执行 ./gradlew install 便会把插件安装在本地 maven 仓库。

        3. 之后在使用的地方引入我们插件的 classpath:

classpath 'com.example.plugin:myplugin:0.0.1'

        4. 之后加载插件:

apply plugin 'myplugin' // 这里的 myplugin 是前面说的 myplugin.properties 的名字

        5. 然后运行 ./gradlew tasks --all | grep mytask,就可以看到我们在 plugin 里新增的 task 了。

        6. ./gradlew mytasks 就可以执行 task 了。

(五)打包发布

        1. 在插件 build.gradle 里新增上传的配置如下:

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "mavenUrl")//具体地址,可以使用本地maven仓库
            pom.version = '0.0.1'
            pom.artifactId = 'myplugin'
        }
    }
}

        2. 然后运行 ./gradlew uploadArchives 就可以了。

(六)调试插件

        那么开发插件的时候如何调试呢?

        1. 首先在 as 中新增一个 remote 配置:

        

         

        2.之后在执行 task 的时候增加下面的参数:

./gradlew app:mytask -Dorg.gradle.debug=true

        

        3. 之后在插件代码中打好断点,在 as 中点击 debug 按钮,就可以调试插件代码了。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值