疯狂Android讲义(一)——第三部分(Gradle详解2)

 

一、本章内容:使用Gradle自动化构建项目(***)

5.Gradle插件和java、application等插件

        前面已经介绍了Gradle的任务和属性,但如果一份构建文件内所有的任务和属性每次执行都需要开发人员重新编写,而且一些复杂的任务总需要开发员重新定义任务类型,那这样Gradle不就和Ant差不多了吗?这就完全体现不出 Gradle的优势了。

        为了简化开发人员编写构建文件的工作, Gradle提供了插件机制。

        开发 Gradle插件很简单,无非就是在插件中预先定义大量的任务类型、任务、属性等(后文会介绍如何开发自定义插件),接下来开发人员只要在 build.gradle文件中使用如下代码来应用插件即可。

apply plugin :插件名

        应用插件就相当于引入了该插件包含的所有任务类型、任务和属性等,这样Gadle即可执行插件中预先定义好的任务。

        前面在介绍 JavaCompile、 JavaExec类型的任务时使用过java插件,引用该插件之后,不仅引入了这两种任务类型,还增加了大量任务。比如在build.gradle文件中仅增加如下代码:

apply plugin :'java'

        然后执行如下命令来査看该构建文件支持的所有任务。 

gradle tasks --all

 执行上面命令,可以看到该构建文件增加了如下任务

  • assemble:装配整个项目。
  • build:装配并测试该项目。
  • buildDependents:装配并测试该项目,以及该项目的依赖项目
  • buildNeeded:与 buildDependents任务基本相同
  • classes:装配该项目的所有类
  • clean:別除构建目录的所有内容。属于Delete类型的任务。
  • jar:将项目的所有类打包成JAR包。属于Jar类型的任务。
  • testClasses:装配所有测试类。
  • init:执行构建的初始化操作
  • wrapper:生成 Gradle包装文件。
  • javadoc:为所有类生成API文档。属于 Javadoc类型的任务。
  • compileJava:编译项目的所有主Java源文件。属于 JavaCompile类型的任务。
  • compileTestJava:编译项目的所有测试Java源文件。属于 JavaCompile类型的任务
  • processResources:处理该项目的所有主资源。属于Copy类型的任务。
  • processTestResources:处理该项目的所有测试资源。属于Copy类型的任务。

        此外,java插件还定义了大量属性,比如 sourceCompatibility属性用于指定在编译Java源文件时所使用的JDK版本; archivesBaseName属性用于指定打包成JAR包时的文件名。

        在默认情况下,java插件要求将项目源代码资源都放在src目录下,将构建生成的字节码文件资源放在 build目录下,该插件会自动管理该目录下的两类源代码和资源。

  • main:项目的主代码和资源。
  • test:项目的测试代码和资源。

        main和test目录都可包含java和 resources两个子目录,其中java子目录存放Java源文件; resources子目录存放资源文件。

        从上面介绍可以看出,main和test目录的内容基本相同,区别只是main目录存放项目的主代码和资源;而test目录存放项目的测试代码和资源。

提示:

        开发者可以在构建文件中通过 sourceSets方法改变项目主代码、资源的存储路径,也可通过该方法改变测试代码和资源的存储路径

        此外,如果项目需要添加第三方或额外依赖的源代码,则可通过 sourceSets方法进行配置。为了配置第三方或额外依赖的源代码路径,需要使用 sourceSets方法,代码如下:

//配置被依赖的源代码路径
sourceSets{
    fkframework
}

        此时可以将 fkframework的Java源代码放在src/fkframework/java目录下,将该框架的资源文件放在src/fkframework/resources目录下。

        Gradle会自动为每一个新创建的 sourceSet创建相应的Task,创建规律为:对于名为“ fkframework”的 sourceSet, Gradle将为其创建 compileFkframeworkJavaprocessFkframeworkResourcesfkframeworClasses这3个Task,它们的作用分别类似于compileJava, processResources和 classes——区别只是前面3个Task处理 fkframework目录下的Java源代码和资源,而后面3个Task处理main目录下的Java源代码和资源。

提示:

        对于main而言,Gradle生成的3个任务并不是compileMainJava、processMainResources和 mainClasses这是由于main是Gradle默认创建的主sourceSet,因此main对应的这个任务就不需要“main”名称了。

通常来说,主项目的源代码和测试代码往往需要依赖第三方项目源代码,因此还需要进行如下配置。

  • 保证Gradle编译第三方项目源代码,编译主项目源代码。因此,需要在构建文件中配置如下依赖(程序清单同上)。
//配置compileJava任务依赖compileFkframeworkJava任务
compileJava.dependsOn compileFkframeworkJava
  • 将第三方项目的字节码文件的存储路径添加到系统编译时、运行时的类路径中。因此,要在构建文件中进行如下配置(程序清单同上)
sourceSet{
    main{
        //将fkframework生成的字节码文件的存储路径添加到 编译时 的类路径中
        compileClasspath = compileClasspath + files(fkframework.output.classesDir)
    }
    test{
        //将fkframework生成的字节码文件的存储路径添加到 运行时 的类路径中
        runtimeClasspath = runtimeClasspath + files(fkframework.output.classesDir)
    }
}

        经过上面配置, Gradle即可将src目录下的 fkframework下第三方框架纳入构建过程中,Gradle在项目构建过程中会先编译第三方项目依的框架,再构建主项目

        读者可以让main目录下的Java类引用 fkframework下的某个Java类,接下来可通过 gradle build命令来构建项目,此时将会看到项目构建成功。

        Gradle还提供了一个 application插件,该插件主要增加了一个 mainClassname属性和run、startScripts等任务,其中 mainClassName属性用于指定该项目的主类,而run任务则用于运行主类。

        在 build.gradle文件中增加如下配置:

apply plugin: 'application'
//指定run任务执行的主类
mainClassName = 'lee.HelloWorld'
//为run任务的classpath增加fkframework的类
run.classpath = sourceSet.main.runtimeClasspath + files(sourceSets.fkframework.output.classesDir)

        上面最后一段代码表示需要为run任务的 classpath增加 fkframework的类,这是由于 fkframework是我们自行添加的 sourceSet,run任务默认不会加载该 sourceSet中的类。

        接下来可以通过gradle run 命令来执run任务,这样即可运行lee.HelloWorld类。

6.依赖管理

        在构建Java项目时通常都要依赖一个或多个框架, Gradle既可像 Maven那样从仓库(远程仓库和本地仓库都行)中下载所依赖的JAR包,也可像Ant那样直接引用项目路径下的JAR包,非常方便

        为 Gradle配置依赖大体上需要两步:

为Gradle配置仓库。配置仓库的目的是告诉 Gradle到哪里下载JAR包
为不同的组配置依赖JAR包。配置依赖JAR包的目的是告诉 Gradle要下载哪些JAR包

        第1步是为了告诉Gradle从哪里下载所依赖的JAR,需要为 Gradle 配置仓库,Gradle可以直接使用 Maven中央仓库,也可以使用 Maven本地仓库,还可以使用本地磁盘路径作为 Maven仓库。

        如果需要使用 Maven中央仓库,那么只要在 build.gradle文件中增加如下配置即可。

//定义仓库
repositories{
    //使用Maven默认的中央仓库
    mavenCenrtal()
}

        有时候在国内通过 Maven中央仓库下载JAR包速度太慢,开发者可能希望使用 Maven中央仓库的国内镜像, Gradle也允许显式指定 Maven仓库的URL。例如如下配置(程序清单同上)。

//定义仓库
repositories{
    //Maven远程仓库(中心仓库)
    maven{
        //显示指定Maven仓库的URL
        url "http://repo2.maven.org/maven2/"
    }
}

        Gradle还允许直接使用本地磁盘路径作为 Maven仓库。例如如下配置(程序清单同上)

//定义仓库
repositories{
    //Maven远程仓库
    maven{
        //显示指定本地磁盘路径作为Maven仓库的URL
        url "g:/abc"
    }
}

        第2步是为了告诉 Gradle需要下载哪些JAR包,并为不同的组配置数量不等的JAR包。可能读者对“组”这个概念感到疑惑,这是由现代软件项目的特征决定的一一很多时候,同一个项目在编译时依某一组JAR包;在运行时可能依赖另一组JAR包;在测试时可能又依赖其他JAR包因此允许使用不同的组来区分不同的JAR包。

        Gradle使用 configurations来配置组。例如如下代码即可配置一个组(程序清单同上)。

configurations{
    //配置名为 fkDependency的依赖组
    fkDependency
}

        接下来就可以为依赖组配置数量不等的JAR包, Gradle使用 dependencies来为依赖组配置JAR包。与在 Maven中指定JAR包的方式相同,配置JAR包同样需要指定3个信息:

  • JAR包所属的组织名:通过 group指定
  • JAR包的名称:通过name指定
  • JAR包的版本:通过 version指定

         如下配置为fkDependency依赖组添加了 commons- logging1.2的JAR包(程序清单同上)。

dependencies{
    //配置依赖JAR包
    fkDependency group:'commons-logging',name:'commons-logging',version:'1.2'
    //简写为如下形式
    fkDependency 'commons-logging:commons-logging:1.2'
}

        上面的一行粗体字代码是简写形式一一也是在实际开发中使用最多的形式,这种形式直接将group、name、 version属性写成一个字符串,中间以英文号隔开即可

        如果在为依赖组添加JAR包之后需要进行额外配置,则可使用闭包。例如如下配置代码。

fkDependency (group:'commons-logging',name:'commons-logging',version:'1.2') {
    //提供额外配置。。。
}
//简写为如下形式
fkDependency ('commons-logging:commons-logging:1.2') {
    //提供额外配置。。。
}

        大部分时候,依赖组总是需要添加多个JAR包,此时既可通过多次调用 fkDependency来添加多个依赖JAR包,也可使用数组形式一次添加多个JAR包。例如如下配置 。

fkDependency 'commons-logging:commons-logging:1.2' , "org.apache.commons:commons-dbcp2:2.2.0"

        在定义好该依之后,接下即可在在务中使用该依了,下面定义一个简单的任务来查看fkDependency所依赖的JAR包(此处以 fkDependency的最后一个配置为准)。

task showFkDependency {
    doLast{
        //输出fkDependency所依赖的JAR包
        println configurations.fkDependency.asPath
    }
}

        使用 gradle showFkDependency命令执行 showFkDependency任务,可以看到如下输出 :

        从上面的输出结果可以看到, Gradle从 Maven中央仓库下载了 commons-logging的JAR包和commons-dbcp2的JAR包,由于commons-dbcp2的JAR包又依赖 commons-pool2的JAR包,因此Gradle还下载了 commons-pool2的JAR包。

        可以看到Gradle是从http://repo2.maven.org/maven2/仓库下载的,这里使用的是第二种方法;如果使用第三种方法(maven { url "g:/abc" } ),执行该任务通常会报错,这是由于g:/abc并不是 Maven仓库的缘故。

        在实际项目开发中,通常并不需要自己配置依赖组,因为当应用java插件之后,该插件默认已经添加了多个依赖组,其中常用的依赖组有如下几个:

  • implementation:主项目源代码(src/ main/java)所依赖的组。这个依赖组很常用。
  • compileOnly:主项目源代码(src/ main/java)编译时才依赖的组
  • runtimeOnly:主项目源代码(src/ main/java)运行时才依赖的组
  • testlmplementation:项目测试代码( src/test/java)所依赖的组
  • testCompileOnly:项目测试代码( src/test/java)编译时才依赖的组
  • testRuntimeOnly:项目测试代码(src/test/java)运行时才依赖的组
  • archives:项目打包时依赖的组

        对于上面的这些常用的依赖组,我们可以根据需要为不同的依赖组添加JAR包。

        假如在项目的源代码中同时用到了commons-logging、commons-dbcp2这两个JAR包的类,为了保障该项目可正常编译、运行,则需要为implementation组增加如下配置。

//以数组形式添加多个JAR包
implementation 'commons-logging:commons-logging:1.2' , "org.apache.commons:commons-dbcp2:2.2.0"

        这样该项目中的Java类即可正常使用这两个JAR包所包含的类。比如在src/ main/java目录下增加一个 Hello World类,该类完全可以使用 commons-logging、 commons-dbcp2所包含的类。

        接下来为项目加 application插件,并通过 mainClassName属性指定主类之后,即可通过gradle run命令运行 HelloWorld类。

7.自定义任务

        自定义任务就是一个实现Task接口的 Groovy类,该接口定义了大量抽象方法需要实现,因此,一般自定义任务类会继承 DefaultTask基类自定义任务的 Groovy类可以自定义多个方法,这些方法可作为Action使用。

        自定义任务的Groovy类既可直接定义在build.gradle文件中,也可使用专门的Groovy源文件定义

        下面先直接在 build.gradle文件中定义一个自定义任务类 。

        上面代码定义了一个 HelloWorldTask类,该类维承了 DefaultTask基类,这就是一个自定义任物类,该自定义任务类可以定义属性作为任务属性,也可以定义方法作为 Action,使用@TaskAction饰的方法将会被自动添加为该任务的 Action

        接下来构建文件定义了hello和 hello1两个任务,这两个任务的类型都是 HelloWorldTask,其中hello任务没有为 message属性指定值,因此该属性将使用默认值:hello1任务为 message属性重新指定了值,因此该属性将会使用新值

        HelloWorldTask类使用@TaskAction修饰了test方法,因此该方法将会被添加成该任务的Action而info方法并未使用@TaskAction修饰,因此需要使用doLast或doFirst方法手动将它添加成Action;否则,该方法不会被执行。

        从上面的输出结果可以看出,在执行 hello1任务时,只有test方法(有@TaskAction修饰)执行。

        上面这种自定义任务的方式虽然简单,但会使得自定义任务类被直接放在 build.gradle文件中,从而导致 build gradle文件比较混乱。当自定义任务较少且比较简单时,可以采用这种方式,但是,当自定义任务较多或比较复杂时,建议采用专门的 Groovy源文件来自定义任务。

        自定义任务的源代码属于项目构建的源代码,因此 Gradle约定将这些源代码放在buildSrc目录下, buildScr目录就相当于另一个Gradle项目, Gradle执行时会先构建buildScr目录下的Groovy源代码以供build.gradle文件使用。

提示:

         buildScr目录就相当于另一个Gradle项目,因此在该目录下还会包含src、build目录,在src目录下也需要定义main子目录,在main子目录下使用groovy子目录保存Groovy源代码。

 

         上面的自定义任务类同样继承了DefaultTask,并包含两个使用@TaskAction修饰的Action方法。从该代码可以看出,将自定义任务类直接放到build.gradle文件中和单独放在Groovy原文件中并没有区别。

        由于上面的自定义任务类指定了包名,因此在 build.gradle中为任务指定类型时也需要指定包名。例如,如下构建代码使用了上面的ShowTask。

//使用 Groovy文件中的task
task show(type:org.fkjava.ShowTask)
//使用 Groovy文件中的task
task show1(type:org.fkjava.ShowTask){
    //对file属性赋值
    file = file("g:/LoggerTest.java")
}

        上面的构建代码定义的show和 show1两个任务的类型都是org.fkjava.ShowTask,其中show任务没有为flie属性指定初始值,因此执行该任务将会打印项目根路径下 dist.txt文件的内容;show1任务的file属性被指定为g:/LoggerTest.java文件,因此执行该任务将会打印G盘中 LoggerTest.java文件的内容

8.自定义插件

         所谓自定义插件,其实就是一个实现 Plugin<Project>接口的类,实现该接口要求必须实现apply( Project project)方法,接下来就可以在该方法中为 Project添加属性和定义任务了一一正如前面所介绍的,所谓插件,无非就是为 Project定义多个属性和任务,这样引入该插件后即可使用这些属性和任务。

提示:

        正如上面所介绍的,项目的插件要实现 Plugin< Project>接ロ,但实际上 Plugin<T>是一个带泛型参数的接口,其中泛型T是可以动态变化的,因此 Plugin<T>不仅可以为项目提供插件,也可以为其他类提供插件

        与自定义任务的 Groovy类相同,自定义插件类既可被直接定义在 build.gradle文件中,也可使用专门的Groovy源文件定义 。

        下面先直接在 build. gradle文件中定义一个自定义插件类

 

         上面第一行粗体字代码定义了一个Plugin< Project>的实现类,这意味着该类是一个自定义插件。自定义插件需要重写apply方法,该方法中的Project形参就代表了引入插件的项目,因此apply方法中的粗体字代码用于为该项目扩展属性。此外,apply方法还为项目定义了两个任务。

        需要说明的是,由于Gradle需要为插件定义的属性生成代理,因此为插件定义的属性类型不能是final类,所以,上面的User类没有使用final修饰。

        接下来即可在构建文件中引入该插件。

         可以看到,上面的showPass输出时,输出了leegang,这是由于引入插件后没有为Project的user.pass属性赋值,因此直接使用默认值。

        与自定义类任务类似的是,自定义插件其实也应该使用专门的Groovy源文件定义,这些用于自定义插件的Groovy类也应该被放在buildSrc目录下。

        上面的插件类同样实现了Plugin<Proiect>接口,并实现了该接口中的apply方法,而 apply方法则可为项目添加属性和任务。该插件只为项目额外定义了一个 showItem任务。
        上面的插件类还使用了一个 Item类,它是一个简单的Groovy类,代码如下。

class Item{
    String name = 'unknown item'
    double price = 0
    double discount = 1.0
}

         定义好插件后,接下来就可以使用apply plugin 引入该插件 ——该插件有包名,因此要使用带包名的类名,并为该插件所扩展的项目属性赋值。

//引入org.fkjava.ItemPlugin插件
apply plugin: org.fkjava.ItemPlugin
//为org.fkjava.ItemPlugin插件所扩展的项目属性赋值
item.name = '疯狂Android讲义'
item.price = 108
item.discount = 0.75

        在引入该插件之后,接下来即可使用调用该插件定义的showItem任务了。使用gradle showItem命令执行showItem任务,可以看到如下输出。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

撩得Android一次心动

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值