Groovy语言介绍

作者: fengcz | 来源:发表于2018-11-14 22:32 被阅读73次

    Groovy概述

    Gradle 采用了 Groovy 语言作为主要的脚本语言 
    
    一个 build.gradle 文件,其实是一个 Groovy 类
    
    Groovy 是一个基于 JVM 的语言,代码最终编译成字节码在 JVM上运行 
    它具有类似于 Java 的语法风格,但是语法又比 Java 要灵活和方便,同时具有动态语言(如 ruby 和 Python)的一些特性。
    

    语言基础

    变量

    有两类可以在生成脚本中声明的变量: 局部变量和额外属性

    • a、局部变量
    在groovy 中,没有固定的类型,局部变量是用def关键字声明的。
    它们只在定义它们的范围内可以被访问  
    def name = 'Jession'
    变量定义时,也可以直接指定类型
    def  int x = 1
    
     单引号与双引号的区别
     单引号中字符串仅仅是字符串,即Java中String
     def name1 = 'Jession'
     双引号可以进行转义,$会被转义成Jession,因此name2的值为Tony,Jessio
     def name2="Tony ,$name1" 
    
    • b、额外属性
    定义:用户可以为增强对象设置额外属性,可以通过对象的ext属性进行添加,读取与设置
    //为myTask创建了一个名字为myProperty属性,值是value
    task myTask{
        ext.myProperty="value"
    }
    

    方法

    使用def关键字定义方法
    指定了函数返回值类型,可以不使用def
    参数类型可以不指定
    方法如果不指定返回值,默认返回最后一行代码的值
    方法可以不用分号结尾
    函数调用也可以不用括号,不推荐(Groovy经常将属性与函数调用混淆)
    
    
    //无类型函数
    def getNum(def num) {
        num * num
    }
    //指定函数返回类型int,可不适用def
    int  getNum(def num){
         num * num
    }
    //参数类型不指定
    def getNum(num) {
        num * num
    21
    }
    

    默认所有的类和方法都是pulic的,可以不用写
    所有类的字段都是private的
    通过new关键字得到类的实例
    使用def接受对象的引用
    def people = new People()
    类中声明的字段都默认会生成对应的setter,getter方法
    people.getName() 等同于 people.name
    

    数据类型

    分类:
      Java的基本数据类型
      Groovy的容器类
      闭包
    
    a.基本数据类型
      Groovy中万物皆对象
      基本数据类型对应的事包装数据类型
      即:int为Integer,boolean为Boolean
    
    b.Groovy的容器类
    
        1).List:链表
         对应Java中的List接口,一般用ArrayList作为真正的实现类
         //定义一个List
         List list = [1, 2, 3, 4, 5]
         //下标2的值为100
         list[2]=100
         //获取下标为2的值
         assert list[2] == 7 
         //内容可以不同类型,并且可以重复,可以为null
         def list3 = ['a', 1, 'a', 'a', 2.5, 2.5f, 2.5d, 'hello', 7g, null] 
         
       2).Map:键-值表,其底层对应Java中的LinkedHashMap。
       //定义一个Map
        def mymap = ['key1':'value1','key2':true]
        //获取map的值
        mymap['key1']
       3).Range:范围,它其实是List的一种拓展。
        //定义,值为1 2 3 4 5
        def range = 1..5
        //定义,值为5 6 7
        Rangerange = 5..<8
      
      c、闭包(Closure)
        闭包是可以用作函数参数和方法参数的代码块,
        代码在某处被定义然后在其后的调用处执行
        简单点说就是代表了一段可执行的代码
       
       定义:
       def xxx = {paramters -> code} 
       def xxx = {无参数,纯code}  //这种情况不需要->
        例:
         //含参数
          def myClosure={String str->
            println(str)
         }
         调用方式:myClosure("Hello")
        //无参数
        def myClosure={
           println("Hello")
        }
        调用方式:myClosure()
       //多参数
        def myClosure={String str,int num->
         println("$str  ,$num ")
        }
       调用方式:myClosure("Hello",20)
      //参数类型可不写
      def myClosure={ str, num->
        println("$str  ,$num ")
    }
    调用方式:myClosure("Hello",20)
    
    如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫it,和this的作用类似。it代表闭包的参数:
       def list = [1,2,3,4,5]  //定义一个List
       list.each{  //调用它的each函数
          println it
      }
    

    Gradle

    Gradle官网 https://gradle.org/

    Project、Task、Action的关系

    创建Android项目的时候,每一个项目中都有一个build.gradle文件,我们称build.gradle文件为构建脚本

    Project:

    每个项目的编译至少有一个 Project,一个 build.gradle就代表一个project
    project由多个task组成

    • Task:
      由多个action组成,action就是一个代码块,里面是需要执行的代码
      比如编译,打包,生成javadoc,发布等

    Gradle对象

    Gradle基于Groovy,Groovy又基于Java。所以,Gradle执行的时候和Groovy一样,会把脚本转换成Java对象
    
    • Gradle三种对象
    • a.Gradle对象:
      当我们执行gradle xxx或者什么的时候,gradle会从默认的配置脚本中构造出一个Gradle对象在整个执行过程中,只有这么一个对象,Gradle对象的数据类型就是Gradle
    • b.Project对象:
      每一个build.gradle会转换成一个Project对象。
      • c.Settings对象:
        每一个settings.gradle都会转换成一个Settings对象。

    例:

    1.在build.gradle文件中定义一个Task,如下:
    task printGradleInfo{
        println "----------------------------------------------- "
        println "In posdevice, gradle id is " +gradle.hashCode()
        println "Home Dir:" + gradle.gradleHomeDir
        println "User Home Dir:" + gradle.gradleUserHomeDir
        println "Parent: " + gradle.parent
    }
    
    2.执行gradlew -q printGradleInfo
    输出
    F:\StudyProject\GradleTest2>gradlew -q printGradleInfo
    In posdevice, gradle id is 635573988
    Home Dir:C:\Users\wangjing\.gradle\wrapper\dists\gradle-2.14.1-all\8bnwg5hd3w55i
    User Home Dir:C:\Users\wangjing\.gradle
    Parent: null
    如果你在打印gradle的hashCode,得到的輸出也是635573988,也验证了在整个执行过程中,只有这么一个对象。
    

    Gradle的生命周期

    • 项目结构


      gradle项目结构gradle项目结构
    每一个Library和每一个App都是单独的Project。
    每一个Project在其根目录下都需要有一个build.gradle。
    build.gradle文件就是该Project的编译脚本。
    因为包含了多个项目,所以还要有一个setting.gradle用于多项目的构建。
    
    Gradle的生命周期总共分成三个阶段,初始化阶段,配置阶段,执行任务阶段
    
    初始化阶段
    
    这个时候settings.gradle会执行
    
    配置阶段
    
    解析每个project中的build.gradle,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系。
    在上图中,gradle的解析顺序是:rootproject 的setting.gradle,然后是rootproject的build.gradle,然后是各个subproject。
    
    执行任务阶段
    
    你在gradle xxx中指定什么任务,gradle就会将这个xxx任务链上的所有任务全部按依赖顺序执行一遍!
    
    

    gradle整个编译过程都是可控的,通过实现TaskExecutionListener和BuildListener可以对整个编译过程进行监听。下面的代码打印了一下task的名字。

    gradle.addListener(new LifecycleListener())
    class LifecycleListener implements  TaskExecutionListener,BuildListener{
      @Override
      void buildStarted(Gradle gradle) {
       }
      @Override
      void settingsEvaluated(Settings settings) {
       }
      @Override
       void projectsLoaded(Gradle gradle) {
       }
      @Override
       void projectsEvaluated(Gradle gradle) {
      }
      @Override
       void buildFinished(BuildResult result) {
      }
      @Override
       void beforeExecute(Task task) {
         println("beforeExecute "+task.name)
        }
       @Override
        void afterExecute(Task task, TaskState state) {
        println("afterExecute  name="+task.name+" state="+state.toString() )
        }
    }
    ===========================
    输出结果:
    beforeExecute printGradleInfo
    afterExecute  name=printGradleInfo state=org.gradle.api.internal.tasks.TaskState
    

    Project

    Project官方文档 https://docs.gradle.org/current/dsl/org.gradle.api.Project.html

    • 一般app.build文件的第一行是apply plugin: 'com.android.application'
    一个 build.gradle 对应一个 Project , apply 是一个Project 的一个函数
    这段代码其实就是调用了project对象的apply方法,传入了一个以plugin为key的map
    完整写出来就是这样的:
    project.apply([plugin: 'com.android.application'])。
    

    我们之前说在 Gradle 中构建脚本定义了一个项目(project)。在构建的每一个项目中,Gradle 创建了一个Project类型的实例,并在构建脚本中关联此Project对象。并且Project接口是你在 Gradle API 中访问一切 的入点,当构建脚本执行时,它会配置此Project对象。调用project的api来获取和项目有关的信息。

    task queryInfo<<{
        println name
        println project.name
    }
    执行命令
    gradlew -q queryInfo
    输出
    queryInfo
    app
    

    第一个获取的是任务名称,第二个获取的是Project名称,如果把queryInfo中的 println name放在外面,他会打印项目名称。

    println name
    task check<<{
    println project.name
    }
    
    结果
    app
    app
    

    查询项目的项目信息:

    task queryProjectInfo<<{
    //项目名
    println project.name
    //项目相对路径
    println project.path
    //项目描述
    println project.description
    //项目的绝对路径
    println project.projectDir
    //项目的build文件绝对路径
    println project.buildDir
    //项目所在的group
    println project.group
    //项目的版本号
    println project.version
    //项目的ant对象
    println project.ant
    }
    

    执行命令

    gradlew -q queryInfo
    

    输出

    其他应用方法

    比如,在解析setting.gradle之后,开始解析build.gradle之前,这里如果要干些事情可以写在beforeEvaluate。
    在所有build.gradle解析完成后,开始执行task之前,此时所有的脚本已经解析完成,task,plugins等所有信息可以获取,task的依赖关系也已经生成,如果此时需要做一些事情,可以写在afterEvaluate。
    

    举列:过滤掉一些我不想执行的task.

    def disableDebugBuild(){
     //project.tasks包含了所有的tasks,下面的findAll是寻找那些名字中带debug的Task。
     //返回值保存到targetTasks容器中
     def targetTasks = project.tasks.findAll{task ->
      task.name.contains("Debug")
      }
       //对满足条件的task,设置它为disable。如此这般,这个Task就不会被执行
        targetTasks.each{
          println"disable debug task  :${it.name}"
           it.setEnabled false
           }
          }
    project.afterEvaluate{
    disableDebugBuild()
    }
    

    又比如

    apply plugin: 'com.android.application'  
    的原形是
    project.apply([plugin: 'com.android.application'])
    

    Task

    • 如果你想知道你多少tasks可以用,直接运行gradlew tasks,其会为你展示所有可用的tasks。
    • 当你创建了一个Android工程,那么将包含Android tasks,build tasks,build setup tasks,help tasks,install tasks,verification tasks等。

    项目构建过程中那么多任务,有些test相关的任务可能根本不需要,可以直接关掉,在build.gradle中加入如下脚本:

    tasks.whenTaskAdded { task ->
    if (task.name.contains('AndroidTest')) {
     task.enabled = false
      }
    }
    

    tasks会获取当前project中所有的task,enabled属性控制任务开关,whenTaskAdded后面的闭包会在gradle配置阶段完成。
    一般我们定义任务的时候采用的是task + 任务名的方式。例如

    task hello << {
          println "hello"
    }
    

    或者

    task(hello)<<{
    println "hello"
    }
    
    task('hello')<<{
        println "hello"
    }
    

    gradle还提供了一个tasks容器来创建任务,通过调用create方法

    tasks.create(name:'hello')<<{
            println "hello"
    }
    

    如何获取一个任务呢?

    将任务看成项目的属性的方式
    println tasks.hello.name
    println tasks['hello'].name
    
    使用tasks容器来定位
    println hello.name
    println project.hello.name
    
    tasks.getByPath()方式来获得
    println tasks.getByPath('hello').path
    println tasks.getByPath(':hello').path
    

    每个Task包含了Action对象的集合。当Task被执行的时候,其内部的Action集合会按次序逐个执行,所以借助doFirst(),doLast()等方法来控制Action在队列中的顺序,同时也是执行的顺序。

    task testAction {
    doFirst {
     println("first")
        }
    doLast {
      println("last")
        }
    }
    输出
    first
    last
    
    其中对于doLast这个Action还有一个简便的写法
    
    task testAction <<{
        println("last")
    }
    <<就代表doLast操作
    
    • task与task之间是有关联的,关联可以使用dependsOn和finalizedBy。
    task A <<{
    println("i am task A")
    task B <<{
      println("i am task B")
    }
    

    A.dependsOn B
    执行gradlew -q A
    输出:

    i am task B
    i am task A
    

    如果是 A.finalizedBy B

    i am task A
    i am task B
    

    相关文章

      网友评论

        本文标题:Groovy语言介绍

        本文链接:https://www.haomeiwen.com/subject/djisfqtx.html