Gradle插件从入门到进阶

您所在的位置:网站首页 maven依赖文件无法定位源码 Gradle插件从入门到进阶

Gradle插件从入门到进阶

2023-05-28 15:02| 来源: 网络整理| 查看: 265

Gradle插件从入门到进阶

技术和职场问题,欢迎关注公众号【Android茶话会】

1、简介

Gradle本身的领域对象主要有Project和Task。Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。另外,一个Task可以读取和设置Project的Property以完成特定的操作。

Groovy 基础

官网

Groovy脚本基础全攻略

groovy使用完全解析

gradle dsl 基础

Android DSL 基础 ASL

相关代码在 github.com/xsfelvis/Gr…

2、核心概念 

Project对象

自定义插件类是通过实现Plugin 接口,并将 org.gradle.api.Project作为模板参数,其中org.gradle.api.Project的实例对象将作为参数传给void apply(Project project)函数,根据官网,可以看出Project是与Gradle交互的主接口,通过Project可以使用gradle的所有特性,并且 Project与build.grale是一对一的关系。简而言之,就是通过代码使用Gradle,通过Project这个入口即可

我们对project的理解更多来源于项目目录中的build.gradle文件(因为它其实就是project对象的委托,在脚本中的配置方法都对应着Project中的API,当构建进程启动后Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,本质上可以认为是包含多个Task的容器,所有的Task都存放在TaskContainer中,Project对象的类图如下所示:

image.png

项目配置

在build.gradle脚本文件中,我们不仅可以对单独project进行配置,也可以定义project块的共有逻辑等,参考下面的定义。

image.png

常见的例子

// 为所有项目添加仓库源配置 allprojects { repositories { jcenter() google() } } // 为所有子项目添加mavenPublish的配置块 subprojects { mavenPublish { groupId = maven.config.groupId releaseRepo = maven.config.releaseRepo snapshotRepo = maven.config.snapshotRepo } }

Task

Gradle Task API

Gradle构建脚本默认的名字是build.gradle,当在shell中执行gradle命令时,Gradle会去当前目录下寻找名字是build.gradle的文件。在Gradle中一个原子性的操作叫做task,简单理解为task是Gradle脚本中的最小可执行单元。

下面是task的类图。image.png

Task的Actions

一个Task是由一序列Action组成的,当运行一个Task的时候,这个Task里的Action序列会按照顺序执行

Task的几种常见写法 task myTask1 { doLast { println "doLast in task1" } } task myTask2 Task :app:third

Task的类型

有copy、jar、Delete 等等 可以参考Doc文档

task copyFile(type: Copy) {   from 'xml'   into 'destination'}

自定义Task

 Gradle 中通过 task 关键字创建的 task,默认的父类都是 org.gradle.api.DefaultTask,这里定义了一些 task 的默认行为。看看下面这个例子:

//自定义Task类,必须继承自DefaultTask class SayHelloTask extends DefaultTask { String msg = "default name" int age = 18 //构造函数必须用@javax.inject.Inject注解标识 @javax.inject.Inject SayHelloTask(int age) { this.age = age } //通过@TaskAction注解来标识该Task要执行的动作 @TaskAction void sayHello() { println "Hello $msg ! age is ${age}" } } //通过constructorArgs参数来指定构造函数的参数值 task hello1(type: SayHelloTask, constructorArgs: [30]) //通过type参数指定task的父类,可以在配置代码里修改父类的属性 task hello2(type: SayHelloTask, constructorArgs: [18]) { //配置代码里修改 SayHelloTask 里的字段 msg 的值 msg = "hjy" }

 执行这两个task

> Task :hello1 Hello default name ! age is 30 > Task :hello2 Hello hjy ! age is 18 Task的类图

Gradle所说的Task是org.gradle.api.Task接口,默认实现是org.gradle.api.DefaultTask类,其类图如下image.png

TaskContainer接口解析

TaskContianer 是用来管理所有的 Task 实例集合的,可以通过 Project.getTasks() 来获取 TaskContainer 实例。

org.gradle.api.tasks.TaskContainer接口: //查找task findByPath(path: String): Task getByPath(path: String): Task getByName(name: String): Task withType(type: Class): TaskCollection matching(condition: Closure): TaskCollection //创建task create(name: String): Task create(name: String, configure: Closure): Task create(name: String, type: Class): Task create(options: Map): Task create(options: Map, configure: Closure): Task //当task被加入到TaskContainer时的监听 whenTaskAdded(action: Closure) //当有task创建时 getTasks().whenTaskAdded { Task task -> println "The task ${task.getName()} is added to the TaskContainer" } //采用create(name: String)创建 getTasks().create("task1") //采用create(options: Map)创建 getTasks().create([name: "task2", group: "MyGroup", description: "这是task2描述", dependsOn: ["task1"]]) //采用create(options: Map, configure: Closure)创建 getTasks().create("task3", { group "MyGroup" setDependsOn(["task1", "task2"]) setDescription "这是task3描述" })

默认情况下,我们常见的task都是org.gradle.api.DefaultTask类型。但是在gradle当中有相当丰富的task类型我们可以直接使用。要更改task的类型,我们可以参考下面的示例

task createDistribution(type:Zip){ }

更多关于task的类型,可以参考gradle的官方文档

www.jianshu.com/p/cd1a78dc8…www.ezlippi.com/blog/2015/0…blog.csdn.net/lzyzsd/arti…

Task的增量构建

Gradle 支持一种叫做 up-to-date 检查的功能,也就是常说的增量构建。Gradle 的 Task 会把每次运行的结果缓存下来,当下次运行时,会检查输出结果有没有变更,如果没有变更则跳过运行,这样可以提高 Gradle 的构建速度。通常,一个 task 会有一些输入(inputs)和一些输出(outputs),task 的输入会影响其输出结果,以官网中的一张图为例:

image.png

图中表示一个java编译的task,它的输入有2种,一是JDK版本号,一是源文件,它的输出结果为class文件,只要JSK版本号与源文件有任何变动,最终编译出的class文件肯定不同的。当我们执行过一次·编译任务之后,再次运行该task,如果发现他的输入没有任何改动,那么它编译后的结果肯定也是不变的,可以直接从缓存里获取输出,这样Gradle会标识该task为UP-TO-DATE,从而跳过该task的执行

TaskInputs、TaskOutputs介绍

如何实现一个增量构建呢,至少指定一个输入,一个输出,Task.getInputs() 对象类型为 TaskInputs,Task.getOutputs() 对象类型为 TaskOuputs,从中也可以看到inputs、outputs都支持哪些数据类型

task test1 { //设置inputs inputs.property("name", "hjy") inputs.property("age", 20) //设置outputs outputs.file("$buildDir/test.txt") doLast { println "exec task task1" } } task test2 { doLast { println "exec task task2" } } //第一次的运行结果 > Task :test1 exec task task1 > Task :test2 exec task task2 BUILD SUCCESSFUL in 0s 2 actionable tasks: 2 executed //第二次的运行结果 > Task :test2 exec task task2 BUILD SUCCESSFUL in 0s 2 actionable tasks: 1 executed, 1 up-to-date

从结果中可以看到,第2次运行时,test1 task 并没有运行,而是被标记为 up-to-date,而 test2 task 则每次都会运行,这就是典型的增量构建。

taskInputs、taskOutputs注解

可以通过task注解来实现增量构建,这是一种更加灵活方便的方式

注解名属性类型描述@Input任意Serializable类型一个简单的输入值@InputFileFile一个输入文件,不是目录@InputDirectoryFile一个输入目录,不是文件@InputFilesIterableFile列表,包含文件和目录@OutputFileFile一个输出文件,不是目录@OutputDirectoryFile一个输出目录,不是文件@OutputFilesMap或Iterable输出文件列表@OutputDirectoriesMap或Iterable输出目录列表 class SayHelloTask extends DefaultTask { //定义输入 @Input String username; @Input int age //定义输出 @OutputDirectory File destDir; @TaskAction void sayHello() { println "Hello $username ! age is $age" } } task test(type: SayHelloTask) { age = 18 username = "hjy" destDir = file("$buildDir/test") }

Property

ext命名空间

Gradle中很多模型类都提供了特别的属性支持,比如Project.在gradle内部,这些属性会以键值对的形式存储。使用ext命名空间,我们可以方便的添加属性。下面的方式都是支持的:

//在project中添加一个名为groupId的属性 project.ext.groupId="tech.easily" // 使用ext块添加属性 ext{ artifactId='EasyDependency' config=[ key:'value' ] }

值得注意的是,只有在声明属性的时候我们需要使用ext命名空间,在使用属性的时候,ext命名空间是可以省略的。

属性文件

正如我们经常在Android项目中看到的,我们可以在项目的根目录下新建一个gradle.properties文件,并在文件中定义简单的键值对形式的属性。这些属性能够被项目中的gradle脚本所访问。如下所示:

# gradle.properties # 注意文件的注释是以#开头的 groupId=tech.easily artifactId=EasyDependency 复制代码

有的时候,我们可能需要在代码中动态的创建属性文件并读取文件中的属性(比如自定义插件的时候),我们可以使用java.util.Properties类。比如:

void createPropertyFile() { def localPropFile = new File(it.projectDir.absolutePath + "/local.properties") def defaultProps = new Properties() if (!localPropFile.exists()) { localPropFile.createNewFile() defaultProps.setProperty("debuggable", 'true') defaultProps.setProperty("groupId", GROUP) defaultProps.setProperty("artifactId", project.name) defaultProps.setProperty("versionName", VERSION_NAME) defaultProps.store(new FileWriter(localPropFile), "properties auto generated for resolve dependencies") } else { localPropFile.withInputStream { stream -> defaultProps.load(stream) } } }

关于属性很重要的一点是属性是可以继承的。在一个项目中定义的属性会自动的被其子项目继承,不管我们是用以上哪种方式添加属性都是适用的。

ExtensionContainer

Extension简介

就是 Gradle 的 Extension,翻译成中文意思就叫扩展。它的作用就是通过实现自定义的 Extension,可以在 Gradle 脚本中增加类似 android 这样命名空间的配置,Gradle 可以识别这种配置,并读取里面的配置内容。

一般我们通过ExtensionContainer来创建Extension,这个类跟TaskContainer命名有点类似。TaskContainer是用来创建并管理Task的,而ExtensionContainer则是用来创建并管理Extension的,通过Project的以下API可以获取到ExtensionContainer对象

ExtensionContainer getExtensions()

简单的Extension /先定义一个普通的java类,包含2个属性 class Foo { int age String username String toString() { return "name = ${username}, age = ${age}" } } //创建一个名为 foo 的Extension getExtensions().create("foo", Foo) //配置Extension foo { age = 30 username = "hjy" } task testExt.doLast { //能直接通过 project 获取到自定义的 Extension println project.foo }

foo 就是我们自定义的 Extension 了,它里面能配置的属性与类 Foo 中的字段是一致的,在 build.gradle 中可以直接通过 project.foo 来访问。每个 Extension 实际上与某个类是相关联的,在 build.gradle 中通过 DSL 来定义,Gradle 会识别解析并生成一个对象实例,通过该类可以获取我们所配置的信息。 Project 有个扩展属性是通过 ext 命名空间配置的,可以看到 ext 与这里是类似的,不同的是 ext 可以配置任何键值对的属性值,而这里只能识别我们定义的 Java 类里的属性值。

ExtensionContainer主要api及用法 T create(String name, Class type, Object... constructionArguments) T create(Class publicType, String name, Class


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3