Kotlin中的Gradle

Grandle程序

第一个Gradle程序

1.创建

左上角【File】->【New Project】->【Project】,出现以下画面,如下勾选

Kotlin中的Gradle_第1张图片

Kotlin中的Gradle_第2张图片

在配置Gradle时,需要连接网络下载资源,不然会出现配置失败的情况。

Kotlin中的Gradle_第3张图片

若在构建完毕后,左侧的路径中没有出现src包,可以在build.gradle文件中最外部添加如下内容(对应的是5.X版本的Gradle):

task 'create-dirs' {
    doLast {
        sourceSets*.java.srcDirs*.each {
            it.mkdirs()
        }
        sourceSets*.resources.srcDirs*.each {
            it.mkdirs()
        }
    }
}

点击右侧栏进行如下操作: 

 Kotlin中的Gradle_第4张图片

2.配置

Gradle是从4.0开始支持Kotlin的,所以一般的Gradle版本要求是要在4.0以上。

默认IDEA使用的是groovy脚本构建的,若想使用Kotlin语言构建,可以将build.gradle文件重命名为build.gradle.kts,为保证修改成功,建议重启IDEA。若build.gradle.kts文件的图标变更,则说明修改成功。

Kotlin中的Gradle_第5张图片

Kotlin中的Gradle_第6张图片

Kotlin中的Gradle_第7张图片

此时将build.gradle.kts文件内容修改为以下内容:

plugins {
    application
}

application{
    mainClassName="com.example.gradleExample.Main"
}

“mainClassName”中对应的是程序入口类的绝对路径,Main类在后续会创建。

3.创建功能类

首先在src/main/java中创建与绝对路径同名的包名。

Kotlin中的Gradle_第8张图片

在该包下分别创建Girl和Main类。

public class Girl {
    public String greeting() {
        return "Hello";
    }
}
public class Main {
    public static void main(String[] args) {
        Girl girl = new Girl();
        System.out.println(girl.greeting());
    }
}

4.运行

打开侧边栏中的Gradle,也可如图中点击选项打开。

Kotlin中的Gradle_第9张图片

 在打开的Gradle Project中,双击run会运行刚才的Main类。

Kotlin中的Gradle_第10张图片

运行结果如图所示。

Kotlin中的Gradle_第11张图片 在运行之后,在distribution文件夹中可见一个distZip标识。

Kotlin中的Gradle_第12张图片

双击该标识可将该程序发布,路径位于build/distributions中。

解压该文件,会出现两个文件夹,分别是lib和bin,其中lib文件夹中有一个jar文件,是程序中的代码编译成的一个jar包;bin文件夹中有两个文件,分别是同名文件和bat文件。

同名文件是linux环境下运行需要的脚本,bat文件是Windows环境下运行需要的脚本。在cmd中,将bat文件拖入窗口回车,会运行刚才我们编写的程序并输出结果。 

Kotlin中的Gradle_第13张图片

Java代码和Kotlin代码共存 

Gradle之前是使用groovy脚本编写的,是一种基于JVM的动态语言,常见的动态语言有Python和JavaScript,动态语言是动态运行的,在编译时编译器无法识别它需要干什么。

后来出现了Kotlin,Kotlin和Java都是静态语言,在之后Gradle开始使用Kotlin进行编写。

1.配置build.gradle.kts文件

为使程序可以编写java代码又可以编写kotlin代码,可对build.gradle.kts文件做如下修改:

plugins {
    application
    kotlin("jvm")
}

application {
    mainClassName = "com.example.gradleExample.Main"
}

dependencies {
    compile(kotlin("stdlib"))
}

repositories {
    jcenter()
}

使用application构建工具可以编写java代码,如果想同时编写kotlin代码,需要依赖kotlin-stdlib.jar包,这个包是kotlin的标准库。

repositories用于声明仓库,在示例中从jcenter仓库中下载依赖;

然后在dependencies节点中,通知jcenter仓库需要依赖(compile)kotlin中的stdlib.jar包;

而在application中的mainClassName声明的是程序的入口,即Main.java

配置build.gradle.kts文件后,在src/main文件夹下创建kotlin文件夹并刷新项目,kotlin文件夹颜色变为蓝色后,该项目就可以运行kotlin文件了。

若刷新项目在jvm处报错,则需要指定版本号。

plugins {
    application
    kotlin("jvm") version "1.3.61"}

application {
    mainClassName = "com.example.gradleExample.Main"
}

dependencies {
    compile(kotlin("stdlib"))
}

repositories {
    jcenter()
}

2.创建Boy.kt

在刚刚创建的kotlin文件夹中,如同刚才在java中,创建一个与绝对路径同名的包,并在该包中创建一个Boy.kt文件。

class Boy(var name: String) {
    fun greeting(): String {
        return name + ": hello"
    }
}

3.修改Main.java

添加代码调用Boy类中的greeting方法。

public class Main {
    public static void main(String[] args) {
        Boy boy = new Boy("Tom");
        System.out.println(boy.greeting());
    }
}

Kotlin中的Gradle_第14张图片


Gradle的任务

Gradle中的project和task

Gradle本身的实体类(又称领域对象)主要有Project和Task,Project为Task提供执行的容器和上下文。Gradle之所以可以工作,是因为在Gradle脚本中将代码以任务的方式插入到了Project中,再由Project来执行这些任务,Gradle就开始工作了。

1.创建

参照前文创建项目名为ProjectAndTask的程序,在build.gradle.kts中创建一个task,代码如下:

task("HelloWorld",{
    println("Hello World!")
})

示例中,task方法接受了两个参数,第一个参数("HelloWorld")是任务task的名称,第二个参数(println("Hello World!"))是一个闭包,这个闭包中编写的是kotlin的代码,是实现任务的内容,在示例中表现为输出一段语句。

2.运行

当写完上述代码后,在侧边栏的Gradle中刷新以下,接着会看见other中出现了我们编写的内容,双击可以运行这个任务。

Kotlin中的Gradle_第15张图片

Gradle任务的依赖

依赖管理是指如果一件事没有完成,则下一件事也没法跟着完成,这两件事就被称为有依赖关系。

比如把苹果放入冰箱这一操作,就需要三个步骤:

  1. 打开冰箱门
  2. 放入苹果
  3. 关上冰箱门

如果冰箱门没打开,那么就没法把苹果放入冰箱,这说明步骤2是依赖于步骤1的,这种依赖关系在代码中是靠dependsOn方法来实现的。下面用示例来演示这一过程。

task("opendoor") {
    println("打开冰箱门")
}

task("putapple") {
    println("放入苹果")
}.dependsOn("opendoor")

task("closedoor"){
    println("关上冰箱门")
}.dependsOn("putapple")

Kotlin中的Gradle_第16张图片

当刷新之后,在Gradle侧边栏出现了三个新建的task(opendoor、putapple、closedoor)。在示例中,后两个task都存在依赖关系:putapple依赖opendoor,closedoor依赖putapple, 而运行这三个task的任意一个,都会得到完整的放入苹果操作。

若在Gradle输出中文出现乱码,可以进行如下操作:

Kotlin中的Gradle_第17张图片

在打开的文件最后一行,输入:

-Dfile.encoding=UTF-8

重启IDEA后生效。

Gradle任务的生命周期 

Gradle任务的生命周期分为“扫描时”和“运行时”。

1.扫描时任务

扫描时任务的特点是扫描时所有任务都会执行,任务执行的先后顺序同build.gradle.kts文件中任务代码顺序一致,谁编写在先谁执行,与依赖顺序无关。

task("opendoor") {
    println("打开冰箱门")
}

task("putelephant") {
    println("放入大象")
}.dependsOn("opendoor")

task("closedoor"){
    println("关上冰箱门")
}.dependsOn("putelephant")

运行这三个任务中的任意一个,得到的结果都是相同的,这是因为在Gradle任务的生命周期在扫描阶段时,会根据任务在build.gradle.kts文件中的顺序来依次执行每个任务。

2.运行时任务

Gradle中的任务默认是扫描时任务,若想使用运行时任务,需要添加doFirst高阶函数来实现。

task("opendoor") {
    doFirst { println("打开冰箱门") }
}

task("putelephant") {
    doFirst { println("放入大象") }
}.dependsOn("opendoor")

task("closedoor") {
    doFirst { println("关上冰箱门") }
}.dependsOn("putelephant")

Kotlin中的Gradle_第18张图片

Kotlin中的Gradle_第19张图片

Kotlin中的Gradle_第20张图片

可见在添加doFirst高阶函数后,运行时任务只执行指定的任务,不执行其他无关的任务。 

3.运行时任务与扫描时任务相辅相成

扫描时任务一般是用于声明变量,运行时任务主要是用于执行程序的业务逻辑。

task("login") {
    val name = "kotlin"
    val pwd = "123"
    doFirst {
        if (("kotlin" == name) and ("123" == pwd)) {
            println("登陆成功")
        } else {
            println("登陆失败")
        }
    }
}

在初始化变量时,默认进入了扫描时任务,此时从先到后声明用户名name和密码pwd;在执行task的内部逻辑时,使用了高阶函数doFirst,此时task只执行doFirst中包含的内容,不执行其他无关任务。因此,运行时任务与扫描时任务是相辅相成的。

Gradle任务集

使用task可以声明一个任务,使用tasks可以声明一个任务集。

任务集可以将所有单独的任务放在一个集合中,便于管理多个任务。

tasks{
    "opendoor"{
        println("打开冰箱门")
    }
    "putelephant"{
        println("放入大象")
    }
    "closedoor"{
        println("关上冰箱门")
    }
}

Gradle默认属性和任务

在Gradle项目中有一些默认的属性和任务,这些默认属性存放在build.gradle.kts文件中的properties集合中,properties是项目中所有默认属性组成的一个Map集合,可以通过forEach高阶函数来遍历并获取集合中的key和value的值。

1.输出Gradle项目中的默认属性及对应的值

使用forEach来输出Gradle项目中所有默认属性以及对应的值。

task("showConfig") {
    val map = project.properties
    map.forEach { (key, value) ->
        println("key=$key,value=$value")
    }
}

Kotlin中的Gradle_第21张图片

在Gradle中的所有默认属性比较多,因此在运行结果中只显示了部分运行结果的内容。

 2.通过命令行运行任务

之前都是用Gradle侧边栏双击需要运行的任务来执行,这一操作属于图形化界面的操作方式。

在进行操作之前,需要对Gradle进行配置。

(1)配置环境变量

首先在Gradle的官网(Gradle | Releases)下载最新的gradle压缩包。

Kotlin中的Gradle_第22张图片

下载完成后解压到自己设定的目录里,记住这一目录。 

右键此电脑,选择属性,点击左侧边栏中的“高级系统设置”。

Kotlin中的Gradle_第23张图片

弹出界面中,选择“环境变量”。

Kotlin中的Gradle_第24张图片

在弹出界面中,在下方的“系统变量”中编辑变量名为“Path”的值,在值后添加路径,该路径为先前解压gradle压缩包的路径。

Kotlin中的Gradle_第25张图片

Kotlin中的Gradle_第26张图片

在添加好之后,通过快捷键win+r,输入“cmd”回车打开命令提示符窗口,输入“gradle -version”并回车,若正确添加环境变量,将会出现如下输出。

Kotlin中的Gradle_第27张图片

(2)通过命令行执行任务

在IDEA的Terminal窗口输入“gradle showConfig”并回车,就可以通过命令行来执行showConfig任务。

但当使用最新版本的gradle时(本文使用的是8.3),可能会出现冲突无法执行showConfig的情况,冲突的点应该是与build.gradle.kts前文的jcenter()有关。

我的解决方法是使用了较低版本的gradle(gradle 4.1)版本,如果读者有更好的办法欢迎举例。在gradle官网往下滚动便可找到此版本,在使用新版本之后需要对环境变量的路径也进行修改。

Kotlin中的Gradle_第28张图片

若在下方没有找到Terminal窗口,可以在顶部栏中【View】->【Tool Windows】->【Terminal】来打开。 

 除了上述方式,也可以直接在build.gradle.kts文件中,通过defaultTasks设置默认的任务来执行。

task("HelloWorld") {
    println("Hello World!")
}

task("showConfig") {
    val map = project.properties
    map.forEach { (key, value) ->
        println("key=$key,value=$value")
    }
}

defaultTasks("HelloWorld","showConfig")

若想用命令行窗口执行Gradle项目中的所有默认任务,可以在Terminal直接输入“gradle”回车。 

Kotlin中的Gradle_第29张图片

Gradle增量式更新任务

Gradle的增量式更新会判断哪些文件发生了改变,哪些文件没有发生改变,他只会冲i性能编译发生改变的代码,这样就提高了编译速度。

1.未使用增量式更新

plugins {
    java
}

task("updateTask") {
    val fileTree = fileTree("src")
    val infoFile = file("info.txt")
    infoFile.writeText("")//清空数据
    fileTree.forEach {
        if (it.isFile) {
            Thread.sleep(1000)
            infoFile.appendText(it.absolutePath + "\r\n")//写入文件
        }
    }
}

示例中,没有使用增量式更新,每次执行updateTask任务时,都会重新遍历src目录并把目录写入info.txt文件中。

2.使用增量式更新

修改上一示例,增量式更新涉及两个新的API:inputs和outputs,分别对应输入和输出。

修改后,只有输入或输出中有改变,才会执行updateTask任务。

plugins {
    java
}

task("updateTask") {
    inputs.dir("src")//输入
    outputs.file("info.txt")//输出
    doFirst {
        val fileTree = fileTree("src")
        val infoFile = file("info.txt")
        infoFile.writeText("")//清空数据
        fileTree.forEach {
            if (it.isFile) {
                Thread.sleep(1000)
                infoFile.appendText(it.absolutePath + "\r\n")//写入文件
            }
        }
    }
}

示例中,调用inputs中的dir方法来指定输入目录src,调用putputs中的file方法来指定输出文件info.txt。

Gradle的依赖

Gradle的依赖包管理

在开发中,一个项目可能需要依赖多个jar包,并且jar包之间可能互有关联,而这也导致这些包都需要同时引入。

例如,在使用commons-httpclient-3.1.jar时,还需要使用commons-logging.jar和commongs-codec.jar包。

而在Gradle项目中,只需要添加commons-httpclient-3.1.jar即可,与之关联的jar包会自动关联。

plugins {
    kotlin("jvm") version "1.3.61"
}

apply {
    plugin("kotlin")
}

dependencies {
    compile("commons-httpclient", "commons-httpclient", "3.1")
}

repositories {
    mavenCentral()
}

点击Gradle侧边栏的刷新按钮后,可见左侧的Project栏,不仅新增了我们添加的jar包,还一并添加了两个jar包,而他们都是与我们添加的jar包相关联的。

Kotlin中的Gradle_第30张图片


公共仓库和依赖配置

公共仓库

在Gradle项目中,存放开发中常用的一些资源的仓库被称为公共仓库。常用的仓库是Maven Center公共仓库和Jcenter公共仓库,其中Maven Center是目前使用最多的公共仓库。

此处我们就以Maven Center为例,进入Maven Center官网(https://mvnrepository.com/),搜索okhttp。

Kotlin中的Gradle_第31张图片

选中Central,在其中寻找需要的版本。

Kotlin中的Gradle_第32张图片 此处我选择了最多人使用的版本,因为我们使用的是Kotlin版本的Gradle,所以需要注意选择。

Kotlin中的Gradle_第33张图片

plugins {
    kotlin("jvm") version "1.3.61"
}

dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.10.0")
}

repositories {
    mavenCentral()
}

 示例中dependencies是用于配置依赖仓库中的jar包,repositories用于配置仓库。

依赖配置

Gradle项目的依赖配置分为两个阶段:编译时依赖测试时依赖

在build.gradle.kts中的compile声明的是编译时依赖,testcompile声明的是测试时依赖。

测试时依赖只在测试阶段使用,在项目打包上线不依赖,这样可使项目的体积变小。

注:compile和implementation的区别

compile被称为过时的方法,取而代之的是api和implementation两个依赖指令。

compile(api)是我们最常用的方式,使用该方式依赖的库将会参与编译和打包。

api在使用上和compile完全相同,可直接替换。

implementation特点在于,对于使用了该命令编译的依赖,对该项目有依赖的项目,将无法访问到使用该命令编译的依赖中的任何程序,也就是将该依赖隐藏在内部,而不对外部公开。

plugins {
    kotlin("jvm") version "1.3.61"
}

apply {
    plugin("kotlin")
}

dependencies {
    compile(kotlin("stdlib"))
    testCompile("junit", "junit", "4.12")
}

repositories {
    mavenCentral()
}

接下来在项目的src/test/java文件夹中创建一个java类,名为Gradle。

public class Gradle {
    @Test//通过注解调用junit测试
    public void test() {
        //断言:当程序执行到断言的位置时,对应的断言应该为真。
        // 若断言不为真时,程序会中止执行,并给出错误信息。
        Assert.assertEquals(true, 1 == 1);
    }
}

 Kotlin中的Gradle_第34张图片

此时测试通过,若将最后一行的判断修改为1==2,则运行结果如下图所示。

Kotlin中的Gradle_第35张图片


Gradle扩展

Gradle插件自定义扩展

在Gradle官网中,声明了很多系统自带的任务,这些任务有Copy、Delete、Checkstyle等。

接下来展示插件自定义扩展的使用方法。

1.扩展Copy任务,实现将src目录中的文件复制到temp目录中

task("myCopy", Copy::class) {
    from("src")
    into("temp")
}

from方法用于声明源目录,into用于声明目标目录,若目标目录不存在,会自动生成这个目录。 

2.扩展Delete任务,实现删除temp目录

task("myDelete", Delete::class) {
    setDelete("temp")
}

3.扩展Jar任务,将class文件生成Jar包并存放在temp目录

import org.gradle.jvm.tasks.Jar
task("myjar", Jar::class) {
    //将out/production/classes目录下的class文件打成jar包存放在temp目录下
    from("out/production/classes")
    include("**/*.class")
    archiveName = "temp/my.jar"
}

include方法主要用于设置文件的类型为class,将out/production/classes目录下的class文件生成my.jar包,并保存到temp目录下。

除了上述任务之外,系统还提供了其他任务,如用JavaCompile来编译Java源码等。开发中需要扩展哪些任务,都可以参考官方文档。

Gradle调用外部扩展

若想在build.gradle.kts文件中执行一段java代码,且这段代码已经写好并生成了一个字节码文件(在java中,被编译器预处理生成的.class文件,被称为字节码文件),此时不想再build.gradle.kts中用groovy或kotlin重写一遍这个代码,而是直接运行。这个时候,Gradle提供了一个闭包Javaexec实现直接运行字节码的功能。

在这个闭包中,main对应的是要运行的类名,classpath对应的是当前的字节码文件存放的位置。

1.生成一个Hello类的字节码

(1)先创建一个名为Order的Gradle项目,在该项目的java文件夹中创建一个Hello类

public class Hello {
    public static void main(String[] args){
        Systerm.out.println("I am java code");
    }
}

(2)修改build.gradle.kts中的内容

plugins{
    application
}

application{
    mainClassName = "Hello"
}

在Gradle侧边栏中的other文件夹中,双击compileJava可以运行Hello类中的main方法,运行成功后,将会在build/classes/java/main目录下生成Hello.class字节码。 

Kotlin中的Gradle_第36张图片

2.在build.gradle.kts中运行Hello.class字节码 

将生成的Hello.class文件复制到与build.gradle.kts同一目录下,在刚才的build.gradle.kts添加如下内容

task("order"){
    //执行java代码
    javaexec{
        main = "Hello"
        classpath(".")
    }
}

Kotlin中的Gradle_第37张图片

你可能感兴趣的:(kotlin,开发语言,java)