疯狂Android讲义(一)——第二部分(Gradle详解1)

备注:下方的*代表这章的重要性。

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

        Gradle是新一代的自动化构建工具。如果读者熟悉Ant、 Maven则可以把Gradle理解为升级版的Ant或 Maven, Gradle可以完成Ant、Maven的所有工作,甚至整合Ant或Maven的功能。

1.下载和安装Gradle

        本书之所以要介绍 Gradle,是因为 Android Studio所采用的构建工具就是 Gradle

        早期 Android采用Ant作为构建工具,后来才使用Gradle取代了Ant。与Ant、 Maven相比,Gradle的优势到底在哪里呢?归纳起来, Gradle的优势有以下几点:

  • Ant、 Maven支持的构建操作, Gradle都可以支持
  • Gradle提供了强大的依赖管理,完全支持已有的 Maven或Ivy仓库
  • Gradle使用 Groovy语言来编写构建文件,构建文件的功能更加灵活。因此, Gradle的构建文件可支持高级API,允许开发人员对构建过程进行监视或配置管理。
  • 使用领域对象模型来描述构建
  • Gradle支持多项目构建
  • 提供简单易用的自定义任务、自定义插件

        下载和安装Gradle请按如下步骤进行:

        ①登录http://services.gradle.org/distributions/站点下载 Gradle最新版 ,下载 Gradle时有3个选择:源代码包(文件名包含src)二进制文件包(文件名包含bin)完整包(文件名包含all),通常建议下载完整包,该包内包含了 Gradle的源代码、二进制文件和文档。

        ②下载gradle-4.10.2-all.zip文件(这里看自己是什么版本的),将下载到的压缩文件解压缩到任意路径下,此处将其解压D:根路径下,解压缩后将看到如下文件结构

  • bin:包含 Gradle的命令——gradle
  • docs:包含用户手册(包括PDF和HIML两种版本)、DSL参考指南、API文档
  • lib:包含 Gradle核心,以及它依赖的JAR包
  • media:主要包含 Gradle的一些图标
  • sample:样例
  • src: Gradle源代码,仅供参考使用。

        ③ Gradle的运行需要JAVA_HOME环境变量,该环境变量应指向JDK安装路径。

        ④ Gradle工具的关键命令就是Gradle解压缩目录下bin路径下的gradle.bat,如果读者希望操作系统可以识别该命令,还应该将Gradle压缩目录下的bin路径添加到操作系统的PATH环境变量中

提示:

        当在命令行窗口、Shell窗口中输入一条命令后,操作系统会到PATH环境变量所指定的系列路径中去搜索,如果找到了该命令所对应的可执行程序,则运行该命令;否则,将提示找不到命令。如果读者不嫌麻烦,愿意每次都输入 D: \gradle-4.10.2\bin\gradle.bat的全路径来运行 Gradle,则可不将 Gradle解压缩目录下的bin路径添加到PATH环境变量中。

        经过上面4个步骤,Gradle安装成功。读者可以启动命令行窗口,输入 gradle. bat 命令,如
看到如下提示信息,则表明 Gradle安装成功。

2.Gradle构建文件和创建任务

        使用 Gradle非常简单,当正确安装 Gradle后,只要输入 gradle或 gradle.bat命令即可。

        如果运行 gradle命令时没有指定任何参数, Grade会在当前目录下搜索build. gradle文件,如果找到了就以该文件作为构建文件,并执行指定任务。

        要想让 Gradle使用其他构建文件,可以用 -- buildfile<构建文件>选项,其中  -- buildfile可以使 -b代替,这两个选项的作用完全一样。例如如下命令:

gradle -b a.xml //显式指定使用a.xml作为构建文件
gradle --buildfile b.xml  //显式指定使用b.xml作为构建文件

        如果希望 Gradle运行时只输出少量的必要信息,则可以使用--quiet或-q选项。
        如果运行 Gradle时要显式指定希望运行的任务,则可以采用如下命令格式。

gradle [task1 [task2 [task3 ]...]]

        实际上,如果读者需要获取 gradle命令的更多详细信息,则直接使用gradle -h选项(或--help选项)即可。

        使用 Gradle的关键就是编写构建文件构建文件的主要作用就是定义构建项目的各种任务(Task)属性,每个任务可包含多个动作( Action), Gradle每次运行时可运行一个或多个任务。

        Gradle构建文件的默认名字为 build.gradle,也可以取其他名字。但如果为该构建文件起其他名字、则意味者着要将这个文件名作为参数传给Gradle工具可以将构建文件放在项目的任何位置,但通常做法是放在项目的根目录中,这样有利于保持项目的简洁和清晰。

        下面是一个典型的 Gradle项目层次结构

<project>://存放整要存放与测试相关的源文件和资源
  ┣━━src:分类存放各种源文件,资源文件
  ┃   ┣━━main:主要存放与 项目 相关的源文件和资源
  ┃   ┃    ┣━━java:存放与项目相关的Java源文件
  ┃   ┃    ┗━━resources:存放与项目相关的资源
  ┃   ┗━━test:主要存放与 测试 相关的源文件和资源
  ┃        ┣━━java:存放与测试相关的Java源文件
  ┃        ┗━━resources:存放与测试相关的资源
  ┣━━build:存放编译后的class文件、资源的文件夹,该文件夹与src具有对应关系
  ┣━━libs:存放第三方JAR包的文件夹
  ┗━━build.gradle: Gradle构建文件

        如果使用 gradle命令构建过项目,那么在项目的根目录下会多出一个.gradle文件夹,在该文件夹中存放的是 Gradle 的构建信息,一般不要手动去修改、删除它。

        Gradle构建文件本质上是一个 Groovy源文件,因此该文件的语法完全符合 Groovy语法一一不过读者不用担心,本节不打算详细介绍 Groovy编程,只是简单介绍 Gradle构建文件的语法。

        Gradle采用领域对象模型的概念来组织构建文件,在整个构建文件中涉及如下最核心的API

  • Project:代表项目,通常一份构建文件代表一个项目。 Project包含大量属性和方法
  • TaskContainer:任务容器每个 Projcct 都会维护一个 TaskContainer类型的 tasks属性。简而言之, Project和 TaskContainer有一一对应的关系。
  • Task:代表 Gradle要执行的一个任务。Task允许指定它依赖的任务、该任务的类型,也可通过 configure()方法配置任务。它还提供了 doFirst()、 doLast()方法来添加 Action。Action对象和 Closure对象都可代表 Action。

提示:

Closure代表一个闭包,所以 Action实际上就代表一个代码块。

        从逻辑上看, Gradle构建文件具有如图1.3所示的结构。

        为 Gradle构建文件创建Task有如下常用方法

  • 调用 Project的task()方法创建Task
  • 调用 TaskContainer 的 createt()方法创建Task。

        不管使用哪种方式来创建Task,通常都可为Task指定如下3个常用属性

  • 通过dependsOn属性指定该Task所依赖的其他Task
  • 通过type属性指定该Task的类型
  • 通过传入的代码块参数配置Task

下面代码示范了创建Task最简单的方式。

//定义hello任务,传入的代码块负责配置该任务
task hello1{
    println "配置第一个任务"
}

        上面代码就是调用task方法定义了ー个名为“hello1”的任务,在创建hello1任务时传入了一个代码块,在该代码块内只包含一行简单的 println加语句。

        接下来即可使用 gradle hello1命令来执行该任务,将可看到如下输出。

        println语句并不是在运行阶段输出的,而是在配置阶段输出的。创建Task时传入的代码块用于配置该Task

        此处需要对 Gradle的构建过程进行说明。Gradle是一种声明式的构建工具,使用Gradle构建时, Gradle并不是直接按顺序执行 build.gradle文件中的内容的, Gradle的构建过程可分为两个阶段

  1. 第一阶段:配置阶段。在此阶段, Gradle会读取 build.gradle文件的全部内容来配置Project和Task,比如设置 Project和Task的 Property、处理Task之间的依赖关系等。
  2. 第二阶段:按依赖关系执行指定Task。

        如果需要为Task添加 Action,则可通过Task的 doFirst、 doLast方法一一正如它们的名字所示的, doFirst用于将 Action添加在 Action序列的前面, doLast用于将 Action添加在 Action序后面

        如下代码示范了为Task添加 Action。

//定义hello2任务、传入的代码块负责配置该任务
task hello2 {
    println "配置第二个任务"
    //调用doLast方法为Task添加 Action
    doLast{
        //使用循环
        for(i in 0..<5){
            println i
        }
    }
    //调用doFrist方法为Task添加 Action
    doFrist{
        //定文变量
        def s = "fkjava.org"
        //输出字符串模板
        println "开始执行第二个任务:$s"
    }
}

        上面代码同样调用task方法定义了一个hello2任务(包括hello1,程序清单一样),该任务的配置代码的第一行是println输出语句:接下来依次使用 doLast, doFirst方法添加了两个Action——正如大家所看到的,使用doLast, doFirst方法添加的 Action就是一个代码块,在这个代码块内完全可定义变量,也可使用循环等流程控制语句,只要这些语句符合 Groovy语法即可。这是多么强大的功能啊——Gradle构建文件不是简单的XML配置,而是完全支持Groovy编程语言,这就可以让 Gradle构建文件增加无限的可能性。

        使用 gradle hello2命令来执行hello2任务,将可看到如下输出:

        在配置阶段, Gradle会配置整个 Project和所有Task,因此可以看到hello1、hello2两个Task的配置代码的执行过程;在运行阶段,则只看到执行 hello2任务。从上面的执行结果可以看出,程序先执行 doFirst方法添加的 Action,再执行 doLast方法添加的 Action。

        此外, Project对象还带一个 TaskContainer类型的tasks属性,因此也可在构建文件中通过 tasks属性的 create方法来创建Task。如下代码示范了使用 tasks属性的 create方法来创建Task(程序清单同上)。

//调用 Project的 tasks属性(Taskcontainer)的 create方法来创建Task
tasks.create(name:'showTasks'){
    doLast{
        //查看 Project的tasks属性的类型
        println "tasks属性的类型:${tasks.class}"
        //遍历 tasks属性
        tasks.each{e ->
            println e
        }
    }
}

        上面代码使用 create方法创建了一个名为“ showTasks”的Task,使用 create方法传入的代码块用于配置该Task——该代码块只是使用 doLast为该Task添加了一个 Action,该 Action用于访问tasks属性的类型,并遍历该构建文件所包含的Task。

        使用 gradle showTasks命令来执行 showTasks任务,将可看到如下输出。

        在创建Task时可通过 dependsOn属性指定该Task所依赖的Task,也可通过type指定该Task的类型一一如果不指定type属性,Task的默认类型是Gradle实现的 DefaultTask

       下面代码使用tasks属性的craete方法来创建Task,并指定 dependsOn和type属性(程序清单同上)。

//调用Project的tasks属性(TaskContalner)的create文件方法来创建Task
//dependsOn指定该Task依赖hello2,该Task的类型是Copy(拷贝文件)
tasks.create(name: 'fktask', dependsOn: 'he1lo2', type: Copy){
    from 'books.xml'
    into 'dist'
}

        上面代码定义了一个名为“fktask”的Task,该Task依赖hello2,且该Task的类型为Copy(完成文件复制的Task,读者可通过 Gradle的API文档查看关于该任务的具体信息),该任务的from方法指定被复制的源文件,into方法指定复制的目标位置,该任务的默认 Action将会完成文件复制。

        使用 gradle fkTask命令来执行 fkTask任务,将可看到如下输出。

        从上面的执行过程可以看出,由于 flask任务依赖 hello2,因此在执行 flask任务之前会先执行hello2任务上面命令执行完成后,可以看到项目根目录下的 books.xml文件被复制到dist目录下。

        使用 Project的task方法创建Task时也可指定type、 dependsOn属性,例如如下代码定义了compile和run两个任务。由于这两个任务分别为 JavaCompile、 JavaExec类型,因此该构建文件要应用java插件。

        构建代码如下(程序清单同上):

        上面代码定义的 compile任务的类型是 JavaCompile(编译Java源程序的Task,读者可通过Gradle的API文档查看关于该Task的具体信息),在使用 JavaCompile时需要通过 source指定源代码所在路径,通过 destinationDir指定编译后的字节码文件的保存位置,该任务的默认 Action将会编译所有Java源文件。

        上面run任务的类型是 JavaExec(运行Java程序的Task,读者可通过 Gradle的API文档查看关于该Task的具体信息),在使用JavaExec时需要通过main指定运行的主类。

        为了看到Gradle编译、运行Java源代码的效果,应按Gradle的约定,将所有Java源文件放在src\main\java目录下,并创建build路径作为构建目录。

        使用 gradle run命令来执行run任务,将可看到 Gradle先执行 compile 任务,然后再执run任务,构建过程生成如下输出。

3.Gradle的属性定义

        除创建任务之外, Gradle构建文件的重要功能就是定义属性, Gradle允许为已Project和Task有的属性指定属性值,也可为Project和Task添加属性。下面依次介绍这几种情况。

        (1)为已有属性指定属性值

        Project、Task都是 Gradle提供的API,它们本身具有内置属性,因此可以在构建文件中为这些属性指定属性值如下代码示范了为 Project、Task内置属性指定属性值。

//为 Project内置属性指定属性值
version =1.0
description= 'Project的属性'
//定义 showProps任务,显示 Project和里Task内置属性
task showProps{
    //为Task内置属性指定属性值
    description='Task的属性'
    doLast{
        println version
        //输出Task属性
        println description
        //由于Task和 Project都有 description属性
        //因此下面要显式指定访间 project的 description属性
        printin project.description
    }
}

         不在任何Task之内,这就是为 Project 定义属性。第三行粗体字代码位于 showProps的初始化代码块内,因此表明为该任务定义属性。

        使用 gradle showProps命令执行上面任务,可以看到如下输出

 Project常用的属性有如下几个,这些属性大部分都可通过上面方式来指定属性值。

  • name:项目的名字
  • path:项目的绝对路径。
  • description:项目的描述信息。
  • buildDir:项目的构建结果的存放路径
  • version:项目版本号

        (2)通过ext添加属性

        如果需要为 Project和Task添加属性,则可通过它们各自的ext进行添加由于 Project和Task都实现了 Extensionaware接口,因此它们都可通过ext来添加属性。

提示:

Gradle中所有实现了 Extensionaware接口的API都可通过ext来添加属性。

如下代码示范了通过ext为Project、Task添加属性(程序清单同上)。

ext.prop1 = '添加的项目属性一'
ext.prop2 = '添加的项目属性二'
//使用ext方法,传入代码块来设置属性
ext{
    prop3 = '添加的项目属性三'
    prop4 = '添加的项目属性四'
}
task showAddedProps{
    ext.prop1 = '添加的任务属性一'
    ext.prop2 = '添加的任务属性二'
    //使用ext方法,传入代码块来设置属性
    ext{
        prop3 = '添加的任务属性三'
        prop4 = '添加的任务属性四'
    }
    doLsat{
        println prop1
        println project.prop1
        println prop2
        println project.prop2
        println prop3
        println project.prop3
        println prop4
        println project.prop4
    }
}

 两种方式:1.通过ext添加属性   2.通过ext方法传入代码块添加属性。

        (3)通过-P选项添加属性

        Gradle还允许在运行gadle命令时通过-P选项来添加属性,使用这种方式添加的属性都是项目属性

task showCmdProp{
    doLast{
        println("系统显卡类型:${graphics}")
        println("系统显卡类型:${project.graphics}")
    }
}

        上面代码在 showCmdProp任务中访问了项目的 graphics属性(加不加 project的效果相同),如果直接运行该任务,将会出现异常,因为 graphics属性不存在。

        可以通过 gradle -P graphics= ATI showCmdProp命令来添加属性,它通过-P选项指定了graphics属性,该属性值为ATI。运行上面命令,可以看到如下输出。

 

         (4)通过JVM参数添加属性

        Gradle也允许在运行 gradle命令时通过JVM参数来添加属性,Java允许通过-D选项为JVM设置参数,使用这种方式添加的属性也都是项目属性。例如定义如下任务(程序清单同上)

task showJVMProp{
    doLast{
        println("添加的JVM属性:${p1}")
    }
}

        上面代码在 showJVMProp任务中访间了项目的p1属性(加不加 project. 的效果相同),如果直接运行该任务,将会出现异常,因为p1属性不存在。
        可以通过 gradle -D org.gradle.project.p1= sss   showJVMProp命令来添加属性,它通过-D选项指定了p1属性,该属性值为sss。运行上面命令,可以看到如下输出。

        从上面运行的命令可以看出,在通过-D选项来添加属性时,每个属性都需要以 "org.gradle.project"为前缀。

        此外, Gradle还允许通过环境变量来添加属性(第五种方法)。由于这种方式比较少见,故此处不再给出详细介绍和示例。

4.增量式构建

        当使用 Gradle构建一个任务时,如果该任务是一个非常耗时的任务,那么每次执行该任务时都需要耗费很长的时间——如果每次执行该任务时都没有造成任何改变,那么重复执行该任务就没有任何意义了。为此 Gradle引入了增量式构建的概念,如果任务的执行和前一次执行比较没有造成任何改变, Gradle不会重复执行该任务,这样就可以提高 Gradle的构建效率。

        那么 Gradle如何判断任务的执行是否造成改变呢? Gradle将每个任务都当成一个黑盒子:只要该任务的输入部分没有改变,输出部分也没有改变一一 Gradle就会判断该任务的执行没有造成任改变。

        Gradle的Task使用 inputs属性来代表任务的输入,该属性是一个 TaskInputs类型的对象;Task使用 outputs属性来代表任务的输出,该属性是一个 TaskOutputs类型的对象。

        不管是 TaskInputs,还是 TaskOuputs,它们都支持设置文件、文件集、目录、属性等,只要它们没有发生改变, Gradle就认为该任务的输入、输出没有发生改变。

        下面示例示范了 Gradle 的增量式构建。

//定义fileContentCopy任务
task fileContentCopy{
    //定义代表 source目录的文件集
    def sourceTxt = fileTree("source")
    def dest = file('dist.txt')
    //定义该任务的输入、输出
    inputs.dir sourceTxt
    outputs.file dest
    doLast{
        //调用File对象的 withPrintWriter方法
        dest.withPrintWriter{ writer ->
            //调用 sourceTxt的each方法遍历每个文件
            sourceTxt.each{
                writer.write(s.text)
            }
        }
    }
}

        上面任务的输入输出代码就是该构建文件的关键代码,其中 inputs.dir sourceTxt指定该任务的输入目录是 sourceTxt,只要该目录没有发生改变、目录里的内容没有发生改变, Gradle就认为该任务的的输入部分没有改变;outputs.file dest指定该任务的输出文件是dest,只要该文件没有发生任何改变, Gradle就认为该任务的输出部分没有改变。 

        上面示例只是为 inputs指定了输入路径,其实同样还可指定输入文件、输入属性等,如果同时为 inputs指定多个输入成分,则只有当所有输入成分都没有发生改变时, Gradle才认为该任务的输入部分没有改变; outputs与此类似,可同时指定输出文件、输出目录、输出属性等。

        第一次执行 gradle fileContentCopy命令,将看到如下输出。

        从上面的输出结果可以看出,第二次执行该命令时不再真正执行该任务,程序只是将该任务更新到最新(up-to-date)。

        如果删除上面构建文件中的两行代码,那么每次执行 gradle fileContentCopy命令时都会真正执行该任务,这意味着没有启用 Gradle的增量式构建。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

撩得Android一次心动

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

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

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

打赏作者

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

抵扣说明:

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

余额充值