使用groovy理解gradle配置文件

groovy语言特点:
1、动态弱类型,不需要明确指定类型,也就是说变量可以以多种方式使用,变量和方法的定义使用的都是def关键字。
2、groovy是动态类型语言,也就是说具体的类型取决于对象本身,而不是声明的引用变量,这样做的好处groovy不要求声明变量的类型,方法参数和方法返回值的类型,具体的类型根据赋值或者返回的参数决定,所以我们可以将类型确定的时间推迟到代码运行时。

1、groovy函数定义

使用关键字fun来定义函数,因为groovy为动态弱类型语言,因此不需要声明参数类型和返回类型。

fun apply(map) {
    map.each {
        println it.key + ":" + it.value
    }
}

函数的调用

def map = [plugin:"com.android.application"]
apply(map)

我们参数传入的是一个map对象,我们其实可以不用提前定义一个map变量,直接将对象传入:

apply(map)
等价于
apply(plugin:"com.android.application")

在groovy语言中,对于函数的调用其实可以省略括号,函数名和参数之间直接使用空格即可,因此上面函数调用等价于:

apply(plugin:"com.android.application")
等价于  
apply plugin: 'com.android.application'

2、groovy闭包

具体参考文章:Groovy闭包简述

groovy闭包可以理解为一个可执行代码块的方法,它是一个对象。

基本格式为:

{ line -> println line}

例子:

def clos = {println("Hello World!")}

闭包的调用有两种:
1、可以把闭包理解为一个方法对象,因此将它作为方法来调用

clos()

2、因为闭包也是一个对象类型,因此可以使用它的一个对象方法来调用

clos.call()

对于有参数的闭包:

def clos = {param ->  println("Hello ${param}!")}
clos("Mirhunana")
clos.call("Mirhunana")

需要注意的是:当只有一个参数传递给闭包 的时候,这个参数的声明是可选的,魔术变量 it 代替了声明

def clos = {println("Hello ${it}!")}

如果有多个参数可以声明多个参数,这个跟方法是相同的

def clos = {param1, param2 ->  println("Hello ${param1} and ${param1}!")}
clos("Mirhunana", "Mirhunana")
clos.call("Mirhunana", "Mirhunana")

我们知道闭包其实就是一个普通的对象类型,因此我们可以跟其他对象的使用方法一样来使用闭包,在方法的调用中,我们当然可以将闭包作为参数来传入。

def defaultConfig(clos) {
    clos.call()
}

调用该函数:

defaultConfig({
    applicationId "com.example.demo.demo"
    minSdkVersion 15
    targetSdkVersion 25
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
})

对于下面这语句,结合我们前面讲的我们应该很容易知道,其他他们就是一些函数的调用了吧。

applicationId "com.example.demo.demo"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

另外,我们知道我们对于函数的调用时可以将括号省略掉的因此,进一步可以得到:

defaultConfig {
    applicationId "com.example.demo.demo"
    minSdkVersion 15
    targetSdkVersion 25
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

读到这里在看看我们Android项目中的build.gradle代码,应该就可以看懂是怎么回事了,原来就是进行的一系列api的调用。

3、Project对象和build.gradle

我们应该知道,build.gradle是对一个Project对象的配置,Project对象与build.gradle是一对一的关系,我们在build.gradle中的所做的一系列调用其实都是针对Project对象进行操作的,也就是前面调用的其实是Project对象的方法来完成对整个项目的配置。

传统上我们都是通过project.apply(map)来实现对project方法的调用,但是在build.gradle脚本中,为什么可以不使用project对象来实现对其方法的调用呢?

Test.groovy文件
==================================================
class DelegateDemo {
    String author = "Mirhunana"

    // 定义一个成员方法,方法的参数为闭包类型
    // 让closure对象成为DelegateDemo对象的一个代理
    def someMethod(closure) {
        println "The original delegate of closure is: ${closure.delegate}"
        closure.delegate = this
        closure()
    }

    def greet(words) {
        println words
    }
}


//下面我们来实现对greet方法的调用
def delegateDemo = new DelegateDemo()
delegateDemo.someMethod {
    // 此处的delegate可以省略
    delegate.greet("Hello, " + delegate.author)
}
=====================================================

上面首先调用someMethod方法,然后使用代理类来实现对greet方法的调用。在调用的过程中可以将delegate省略,即:

delegateDemo.someMethod {
    greet("Hello, " + delegate.author)
}

输出:

The original delegate of closure is: Test@1700915
Hello, Mirhunana

这样就实现了不需要delegateDemo对象,就可以对其方法进行调用。

另外,closure的delegate的默认值就是调用以closure为参数的方法所在context中的this,看到上面默认打印的代理对象为Test@1700915,原因就是我们someMethod方法的调用所处的context是Test.groovy文件。

如果我们希望默认delegate为delegateDemo对象,我们可以做如下修改:

==================================================
class DelegateDemo {
    String author = "Mirhunana"

    def someMethod2(closure) {
        println "The original delegate of closure is: ${closure.delegate}"
        // 无需设置closure.delegate
        // 因为closure的delegate默认值已经是DelegateDemo.this
        closure()
    }

    def greet(words) {
        println words
    }

    def test() {
        def delegateDemo = new DelegateDemo()

        // 可以看到以闭包对象为参数的方法的调用是在DelegateDemo里面
        // 所以闭包的默认delegate为delegateDemo对象
        delegateDemo.someMethod2 {
            // 省略了delegate
            greet("Hello, " + author)
        }
    }
}

//下面我们来实现对greet方法的调用
def delegateDemo = new DelegateDemo()
delegateDemo.test()
==================================================

那么我们build.gradle里面是怎么实现对project对象方法的调用的呢?

class Project {
    def with(closure) {
        closure.delegate = this;
        closure();
    }

    def apply(map) {...}
    def android(closure) {...}
    // ...
}

def project = new Project()
project.with {
    // build.gradle内容
    // 例如:
    // apply plugin: 'com.android.application'
    // android {
    //   compileSdkVersion 25
    //   buildToolsVersion '25.0.0'
    //   defaultConfig {
    //      applicationId "com.example.demo.demo"
    //      minSdkVersion 15
    //      targetSdkVersion 25
    //      versionCode 1
    //      versionName "1.0"
    //      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    //   }
    //   buildTypes {
    //        release {
    //             minifyEnabled false
    //             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    //        }
    //    }
    //  }
  }

可以看到闭包内的内容就是build.gradle对project对象的操作。这样我们在闭包里面就可以调用project里面的方法了,而且不需要显示的使用project。所以,我们build.gradle里面的内容其实最终转化为一个闭包方法中的内容。

最终附上Android项目中build.gradle源码,看完上面的解析之后,再来看这个脚本,是不是感觉很清晰,其实build.gradle脚本的配置就是进行一系类函数api的调用过程。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion '25.0.0'
    defaultConfig {
        applicationId "com.example.demo.demo"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.0', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
    testCompile 'junit:junit:4.12'
    compile 'fm.jiecao:jiecaovideoplayer:5.5.2'
}

注意:上面只是根据自己的理解来演示原理,仅供参考,与gradle底层的实现可能会有些出入。

参考文章:
http://www.cnblogs.com/dasusu/p/6635576.html
http://benweizhu.github.io/blog/2015/03/31/deep-into-gradle-in-action-6/
http://www.blogjava.net/BlueSUN/archive/2007/12/22/169580.html

欢迎关注微信公众号:DroidMind
精品内容独家发布平台


呈现与博客不一样的技术干货

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值