转载:http://www.apkbus.com/forum.php?mod=viewthread&tid=255064&extra=page%3D2%26filter%3Dauthor%26orderby%3Ddateline&_dsign=276e9e2e

 
相信看过前一篇 《Android Studio 与 Gradle 深入》的同学,有一部分就会遇到我初识 Gradle 时的困惑:代码我也依稀看得懂,但就是不知道还能这样写,为什么这样写。
 

问题与解决方案

回想我在 Gradle 的学习过程中遇到的问题与及其解决方案,总结出下面三点:
 
  • 原理不懂:学习 Groovy 与 Gradle 的基础原理
  • Gradle 实践不懂:学会找示例,学习开源例子
  • 方法和属性不懂:学会查文档
下面的我将以解决三个问题为线索,介绍 Groovy 和 Gradle。
 

学习 GroovyGroovy 概述

Gradle 采用了 Groovy 语言作为主要的脚本语言。一个 build.gradle 文件,其实是一个 Groovy 类。
Groovy 是一个基于 JVM 的语言,代码最终编译成字节码(bytecode)在 JVM 上运行。它具有类似于 Java 的语法风格,但是语法又比 Java 要灵活和方便,同时具有动态语言(如 ruby 和 Python)的一些特性。
Groovy 的诸多特定,很适合用来定义 DSL(Domain Specific Language)。
简单的来讲 DSL 是一个面向特定小领域的语言,如常见的 HTML、CSS 都是 DSL,它通常是以配置的方式进行编程,与之相对的是通用语言(General Purpose Language),如 Java 等。
既然是一门语言,就肯定有自己的特性。我们要从下面几个步骤来介绍 Groovy:
 
  • 环境安装
  • 语言基础
  • 语言特性及其本质

环境安装

Groovy 官方安装文档提供多种方式进行安装,确保你不会在跪在环境配置的路上 ^-^ :
  • Windows 下推荐 binary 包配置环境变量
  • Mac 下推荐使用 sdkman 或者 Brew 进行安装
  • Linux 下推荐 sdkman
  • 嵌入在程序中,则推荐使用 Maven 远程依赖
初学者也没有必要使用IDE,平添障碍,后期用 Intellij IDEA Community 版本足矣。
 
下面只介绍 Mac 下使用 sdkman 的安装方式。
 
  • 下载安装 sdkman,执行下面命令,按照提示安装即可
1 $ curl -s http://get.sdkman.io | bash
  • 使环境变量生效
1 $ source "$HOME/.sdkman/bin/sdkman-init.sh"
  • 安装 Groovy
1 $ sdk install groovy
  • 查看当前版本,如果能否运行且输出对应版本,就是成功了
1
2
$ groovy -version
Groovy Version: 2.4.4 JVM: 1.8.0_25 Vendor: Oracle Corporation OS: Mac OS X

初探

安装好环境之后,先来一个 hello, world!
 
  • 新建文件
1 $ vim test.groovy
  • 在其中写上内容
1 println "hello, world!"
  • 保存退出,执行
1
2
$ groovy test.groovy
hello, world!
Wow, So easy!
 

语言基础

下面将会用一些实际的例子,介绍一些最重要的点,
 
例子都已经传到 github 的 demo 项目中。
第一次使用 demo 项目的时候,需要等待自动下载几个远程包。
笔者一个 Java 程序员,可以你能够看到很多 Java 的习性还是留在代码中。
 

文件与类,变量与函数

Groovy 代码文件,支持不显式声明类:
 
ScriptClass.groovy
 
1 println 'hello,world'
这样一个 Groovy 脚本,经过编译之后,会产生一个继承自 groovy.lang.Script 类的子类:
 
是不是能看出点什么?
 
groovy/build/classes/main/io/kvh/as/groovy/ScriptClass.class
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ScriptClass extends Script {
    public ScriptClass() {
        CallSite[] var1 = $getCallSiteArray();
    }

public ScriptClass(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }

public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].call(InvokerHelper.class, ScriptClass.class, args);
    }

public Object run() {//关键方法
        CallSite[] var1 = $getCallSiteArray();
        return var1[1].callCurrent(this, "hello,world");// got it?
    }
}

Groovy 支持如下的方式来定义变量和函数:
 
VarAndMethod.groovy
1
2
3
4
5
6
7
8
9
10
def varAndMethod() {
    def a = 1//不显式声明变量类型
    a = "abc"//运行时改变类型

println a//无需;结束一行代码
    a = 4//最后一行作为返回值
}
def ret = varAndMethod()//文件内运行方法

println ret//输出4

字符串

Groovy 支持单引号,双引号,三单引号声明一个字符串;
 
Quoted.groovy
1
2
3
4
5
6
7
8
9
10
def quoted() {
        def singleQ = 'hello, single quot'// 声明为java.lang.String
        def doubleQ = "hello, double quot ${singleQ}"// 如果有${},则为groovy.lang.GString,支持变量替换;否则为java.lang.String
        def tripleQ = '''hello,
triple quot'''// 允许多行,而不需要+号

println singleQ
        println doubleQ
        println tripleQ
}

Groovy 还支持以:
 
1
2
3
"""..."""
/.../
$/.../$
来声明字符串,详情参见参考文档。
 

List,Array 和 Map

Groovy 默认使用 java.util.ArrayList 来提供 List 服务,但提供了更加灵活易用的操作方式:
 
Collections.groovy
 
1
2
3
4
5
6
7
def playList() {
    def lst = ["a",2,true]//支持不同类型元素

println(lst)
}

playList()

要使用 Array,需要显式声明:
 
1
2
3
4
5
6
7
8
9
def playArray() {
    def intArr = [1, 2, 3] as int[]//显示声明
    String[] strArr = ["a", "b"]//另外一种方式

println(intArr)
    println(strArr)
}

playArray()

使用 key:value 的方式定义 Map,注意 key 的正确使用方式:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def playMap() {
    def map = [a: "a", b: "b"]
    
    println(map)
    
    def key = "name"
    def map2 = [key: 'a']//未使用
    def map3 = [(key): 'a']//使用

println(map2)
    println(map3)
}

playMap()

import

Groovy 提供了更强大的 import
 
  • 默认 import,这些类都是被默认 import 到代码中的,可以直接使用
1
2
3
4
5
6
7
8
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
  • import alias
引入一个类,通过 as 关键字赋予一个别名,有点 JavaScript 的意思么?
 
Import.groovy
 
1
2
3
import java.lang.String as KString

println(new KString("aaa"))

语言特性及其本质Closure(闭包)

闭包的概念不再赘述,大概就是可以将函数作为参数传递和使用,详情参见 wikipedia
 
1 { [closureParameters -> ] statements }
可以省略方括号内的内容,也就是说,可以没有参数列表。
 
Closure.groovy
 
当闭包不声明参数列表,默认参数是 it;闭包被定义之后,是一个 Closure 对象,可以对其调用 call 方法使其执行。
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def defaultIt() {
    3.times {
        println it //默认参数 it
    }
}

defaultIt()
def closureObj() {
    def obj = { a ->
        ++a
    }
    println obj.call(1)
}

closureObj()

面向对象特性

  • 定义和实例化一个类
GroovyCLass.groovy
 
1
2
3
4
5
6
7
class People{
    String name
    int age
}

People p1 = new People();
People p2 = new People(name:"Luis",age: 29)//通过类似 map 的方式赋值参数

  • 方法的默认参数
1
2
3
4
5
6
def foo(String p1, int p2 = 1) {
    println(p1)
    println(p2)
}

foo("hello")

  • Field 和 Property
Field 是以各种修饰符修饰的变量。Property是私有变量和自带的 gettters/setters,
下面的类具有私有变量 name、age,并自带这两个变量的 getter 和 setter:
 
1
2
3
4
class People{
    String name
    int age
}
当变量声明为 final 的时候,默认就没有 setter
 
  • Trait
Groovy 提供了一个叫做 Trait 特性实现了多继承,还有很多强大的功能,读者可以自己探索。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
trait Fly {
    void fly() {
        println("fly")
    }
}

trait Walk {
    void walk() {
        println("walk")
    }
}

class Duck implements Fly, Walk {

}

Duck duck = new Duck()
duck.fly()
duck.walk()

Groovy 基础小结

至此,我们已经熟悉了 Groovy 的基本语法和特性,相信你也能够使用 Groovy 写一些基础程序了。Groovy 还有很多深入的内容,请用到的时候,参考这本这个 pdf: 《Programming Groovy 2》
下面开始介绍使用 Groovy 写 Gradle 程序,主要的内容来自 《Gradle Sser Guide》
 

学习 GradleGradle 安装

三种方式安装 Gradle:
1 brew install gradle
  • 推荐:使用 IntelliJ IDEA(Android Studio)自带的 wrapper 结构来下载 Gradle
Wrapper 是为了让不同版本的插件能够使用其对应版本的 Gradle 的一个机制
Gradle Wrapper 会把不同的版本 Gradle 安装在:
 
1 $USER_HOME/.gradle/wrapper/dists

Gradle Build 的生命周期

回忆一下《Android Studio 与 Gradle 深入》中的 Android Studio 项目文件结构:
 
 
1
2
3
4
5
6
7
8
.
├── app                                                //app module
│   ├── build.gradle                //app module 的 build.gradle
├── build.gradle                        //项目 build.gradle,通常配置项目全局配置,如 repositories 和 dependencies
├── gradle.properties                //项目属性文件,通常可以放置一些常量
├── lib                                                //lib module
│   ├── build.gradle                //lib module 的 build.gradle
└── settings.gradle                        //项目总体设置,通常是配置项目中所有的 module

Gradle 构建三个阶段:

  • 初始化:Gradle 支持单 module 构建和多 module 构建(Android Studio 创建的项目默认是多 module)。初始化阶段,Gradle 会为每一个 module 中的 build.gradle 文件创建一个 Project 实例。
  • 配置:项目根目录的 build.gradle 会首先被执行
  • 执行:执行所选取的 task

Settings.gradle

多 module 构建要求在项目根目录下有一个 settings.gradle,用来指定哪些 module 参与构建,如:
settings.gradle
 
1
2
3
include ':app', ':groovy'

println 'print in settings.gradle'

在 settings.gradle 文件中,添加一行打印语句,在控制台中,切换到当前项目根目录下执行:
 
1 ./gradlew -p groovy build
可以在看出 settings.gradle 的代码每次都会率先执行。
 

Task

接下来,我们开始学习 Gradle 的核心 Task。
groovy/build.gradle
定义一个 Task:
 
1
2
3
4
5
task hello {
    doLast {
        println 'Hello,'
    }
}
执行命令,查看输出:
 
1
2
$ ./gradlew hello
Hello,
Task 也可以这样定义:
 
1
2
3
task World << {
    println 'World!'
}
注意,如果定义成这样:
 
1
2
3
task hi {
    println 'description hi'
}
在进行初始化和配置的时候,下面语句就会运行。
 
1 println 'hi'
这种语法通常是用来定义 task 的描述信息。
Task 可设置 dependsOn 和 finalizedBy:
 
1
2
3
4
5
6
7
8
9
10
11
task hello {
    doLast {
        println 'Hello,'
    }
}

task intro(dependsOn: hello) << {
    println 'intro'
}

World.finalizedBy hello

执行 intro 之前,会先执行 hello;执行 World 之后,会自动执行 hello。

Plugin

Gradle 的核心代码,只提供了一个框架,具体的功能(如构建 Android 工程)是通过插件机制来实现的。
Gradle 提供了大量官方的插件,如 Maven、Groovy、Java、Publishing、Signing等,也有大量第三方的插件(Android),甚至每个人都可以自己实现一个插件(如 笔者开发的 Bugtags 插件,这个将在最后一篇讲述)。
这些 plugin 定义了一系列的 task、DSL 和约定,在build.gradle 文件使用这些 plugin:
 
1 apply plugin: java
当你写了一个独立的 file_uri.gradle 文件,你可以通过:
 
1 apply from: 'file_uri.gradle'
来引入你的 gradle 文件,这个文件甚至可以在某个服务器上。
 

Gradle 实践参考

学习了基础理论之后,如果你还是不知道如何开始写,那就先来实现一个自定义 apk 名称的功能吧!
 
1
2
3
4
5
6
7
8
9
10
11
12
13
android.applicationVariants.all { variant ->//获取 variant 参数,就是 productFlavor x buildType
    variant.outputs.each { output ->//获取输出文件
        def file = output.outputFile//修改实例
        output.outputFile = new File(
                (String) file.parent,
                (String) file.name.replace(
                        file.name,
                        // alter this string to change output file name
                        "Your_Apk_Name_" + variant.name + "_" + variant.versionName + ".apk"
                )
        )
    }
}
你问我怎么知道 android 下有个 applicationVariants?其实我也不知道的,也得找文档。
 
因为使用的是 Android 的插件,那就得在谷歌搜 “android gradle plugin dsl”,果然有个 Android Plugin DSL Reference
 
点进去找找,里面有关于 build variant 的文档: applicationVariants,既然是一个 Set,那就可以调用 all 方法。
 
写代码调试,再配合文档,你就晓得该怎么写了。
 

总结

从 Gradle 入门到现在略懂,经历了大量懵懂的时光。最后狠下心去系统学习了 Groovy 和 Gradle 的基础之后,最终茅塞顿开。希望读者遇到类似的情况,一定要沉下心,多学多练。
在接下来的两篇,我将分别介绍将发布远程库和编写 Gradle 插件。

Android studio:Groovy 与 Gradle 基础【三】的更多相关文章

  1. 拥抱 Android Studio 之五:Gradle 插件开发

    实践出真知 笔者有位朋友,每次新学一门语言,都会用来写一个贪吃蛇游戏,以此来检验自己学习的成果.笔者也有类似体会.所谓纸上得来终觉浅,绝知此事要躬行.这一章,笔者将以开发和发布一个 Gradle 插件 ...

  2. 关于Android Studio里的Gradle,你所需要知道的都在这里了

    Gradle介绍 Gradle是一个先进的build toolkit,可以方便的管理依赖包和定义自己的build逻辑.到底有多先进,Android Studio官方集成Gradle,Google还专门 ...

  3. [Learn Android Studio 汉化教程]第三章:使用 Android Studio 编程

    [Learn Android Studio 汉化教程]第三章:使用 Android Studio 编程 本章包含如何在 Android Studio 中书写或生成代码. Android Studio ...

  4. android studio中为gradle指定cmake版本

    Android Studio相当于是Intellij基础上写了一个AS插件,这个插件使用gradle作为构建系统,因此构建出现问题先考虑gradle的文档. gradle可以使用native buil ...

  5. 【Android Studio安装部署系列】三十、从Android studio2.2.2升级到Android studio3.0之路

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 Android Studio 3.0的新功能 https://mp.weixin.qq.com/s/2XmVG4mKEDX6-bvZ ...

  6. Android Studio之回退Gradle版本方法

    Android Studio之回退Gradle版本方法 (Minimum supported Gradle version is 4.10.1. Current version is 4.6.)   ...

  7. Android : Android Studio 更新至gradle 4.10.1后Variants API变化

    同步警告: WARNING: API 'variantOutput.getPackageApplication()' is obsolete and has been replaced with 'v ...

  8. 【已解决】Android Studio下,gradle project sync failed 错误

    原文:[已解决]Android Studio下,gradle project sync failed 错误 Android studio下突然报错 gradle project sync failed ...

  9. Android studio第一次使用配置(三)gradle项目构建

    1.gradle的概念 2.gradle配置jar包.和libs目录导入jar包的差别 3.签名打包: (1)Studio (2)命令行 (3)gradle wrapper的原理 4.BuildCon ...

随机推荐

  1. React Native 项目运行在 Web 浏览器上面

    React Native 的出现,让前端工程师拥有了使用 JavaScript 编写原生 APP 的能力.相比之前的 Web app 来说,对于性能和用户体验提升了非常多. 但是 React Nati ...

  2. CefSharp的引用、配置、实例

    CefSharp的引用.配置.实例与报错排除(源码) Winform下CefSharp的引用.配置.实例与报错排除 本文详细介绍了CefSharp在vs2013..net4.0环境下,创建Winfro ...

  3. R统计分析处理

    [翻译]Awesome R资源大全中文版来了,全球最火的R工具包一网打尽,超过300+工具,还在等什么? 阅读目录 0.前言 1.集成开发环境 2.语法 3.数据操作 4.图形显示 5.HTML部件 ...

  4. Linux 网络编程详解十

    select int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *tim ...

  5. java 中的异步回调

    异步回调,本来在c#中是一件极为简单和优雅的事情,想不到在java的世界里,却如此烦琐,先看下类图: 先定义了一个CallBackTask,做为外层的面子工程,其主要工作为start 开始一个异步操作 ...

  6. hadoop1.2.1伪分布模式配置

    1.修改core-site.xml,配置hdfs <configuration> <property> <name>fs.default.name</name ...

  7. 前端见微知著JavaScript基础篇:this or that ?

    上节,我们提到了this关键字的问题,并且追加了一句很有意义的话:谁调用我,我指向谁.的确,在javascript中,在默认情况下,this会指向一个已经初始化的window对象.所以你不论有多少全局 ...

  8. FineUI v4.0.3 (beta) 和 FineUI v3.3.3 发布了!

    关于FineUI基于 ExtJS 的开源 ASP.NET 控件库 FineUI的使命创建 No JavaScript,No CSS,No UpdatePanel,No ViewState,No Web ...

  9. 判断 JS 中对象的类型

    1.typeof 形如 var x = "xx"; typeof x == 'string' typeof(x) 返回类型有:'undefined' “string” 'numbe ...

  10. Theano2.1.21-基础知识之theano中多核的支持

    来自:http://deeplearning.net/software/theano/tutorial/multi_cores.html Multi cores support in Theano 一 ...