Groovy 和 Gradle (Android Studio)基础

来源:互联网 发布:淘宝怎样报名天天特价 编辑:程序博客网 时间:2024/06/07 10:19

1 参考

  • 深入理解Android之Gradle: http://blog.csdn.net/innost/article/details/48228651

  • Gradle完整指南(Android):http://www.jianshu.com/p/9df3c3b6067a

  • Gradle学习系列:http://www.kancloud.cn/digest/itfootball-gradle/

  • Gradle官方网站:https://gradle.org/

  • Groovy的API文档:http://www.groovy-lang.org/api.html

2 资源

  • Gradle 各版本下载:http://services.gradle.org/distributions/
  • Gradle_Recipes_for_Android.pdf,Android构建的实用文档: http://pan.baidu.com/s/1kVnq4WB
  • Building_and_Testing_with_Gradle.pdf,通用的构建与测试文档: http://pan.baidu.com/s/1jIM4wgI
  • Gradle User Guide.pdf,官方给出的使用手册: http://pan.baidu.com/s/1qYIozFm

3 基本

  • Gradle虽然使用的是Groovy语言,但是其实际上是基于Java语言的,所以在编写脚本的时候可以直接使用Java语言。

4 Groovy语言基础

虽然在编写脚本的时候可以直接使用java代码,但是使用脚本化语言更加的清晰。

Groovy开发环境配置

参看:http://www.jianshu.com/p/777cc61a6202

前置基础知识

  • Groovy注释标记和Java一样,支持//或者/**/

  • Groovy语句可以不用分号结尾

  • Groovy中支持动态类型,即定义变量的时候可以不指定其类型,Groovy中,变量定义可以使用关键字def,注意,虽然def不是必须的,但是为了代码清晰,建议还是使用def关键字

  • Groovy中每一个对象的成员变量会自动为它生成gettersetter函数,所以我们可以直接调用其成员变量或者对应方法。

    var0 = "no def no type"    def var1 = 1   //可以不使用分号结尾      def var2 = "I ama person"      def int x = 1  //变量定义时,也可以直接指定类型
  • 函数定义时,参数的类型也可以不指定,比如
    String testFunction(arg1,arg2){//无需指定参数类型      ...      }
  • 除了变量定义可以不指定类型外,Groovy中函数的返回值也可以是无类型的。比如:
    //无类型的函数定义,必须使用def关键字      def  nonReturnTypeFunc(){          last_line   //最后一行代码的执行结果就是本函数的返回值      }      //如果指定了函数返回类型,则可不必加def关键字来定义函数      String getString(){         return"I am a string"      }
  • Groovy中的函数调用可以不加括号,但是在定义函数的时候不可以省略,否则会被编译器认为是变量
println("123")   ->    println "123"

对字符串的支持

  • 单引号”,表示严格的字符串,等同于java的String类型
  • 双引号”“,与普通字符串的区别是会对 $表达式先求值
def var = 123println "i am a $var"  // 最后打印 i am a 123
  • 三个引号”’xxxx”’中的字符支持随意换行
str = ''' 1223          122          556      '''

五种特殊运算符

  • ?.运算符,相当于不为空,则执行某个操作
list?.size()//等同于if(list != null){    list.size()}
  • *.运算符,用于对集合类型的对象的每个元素执行相同的方法,返回值为大小和该集合元素相同的List。
def smaple = ['123','1','12345']println smaple*.size() 
  • .&方法作为一个闭包参数传递
def list = {1, 2, 3}list.each{    println it}int printNumber(int number){    println number}//作为方法作为一个闭包传递list.each(this.&printNumber)
  • .@直接调用字段,在groovy中直接使用.调用的是对应的getter方法
class Todo {      String name      def getName() {        println "Getting Name"        name      }  }  def todo = new Todo(name: "Jim")  println todo.name  println todo.@name  ========  result:  Getting Name  Jim  Jim 
  • ?:替代Java的三目运算符
String name=  person.name ? person.name : 'unknown'// java的写法  def name2= person.name ?: "unknown" // Groovy 的写法

Groovy中的数据类型

主要介绍一下三种数据类型:

  • Java中的基本数据类型
  • Groovy中的容器类
  • 闭包

基本数据类型

Groovy作为动态语言,其中一切事物都是对象,所以即使是基本数据类型,其也是被转化为相应的对象的。

def int var = 123println var.getClass().getName()  //打印的结果为 java.lang.Integer

容器类

Groovy中的容器类就三种:

  • List:链表,其底层对应Java中的List接口,一般用ArrayList作为真正的实现类。
  • Map:键-值表,其底层对应Java中的LinkedHashMap。
  • Range:范围,它其实是List的一种拓展。
List
//定义list变量,使用[]方式来定,其元素可以是任何的对象def testList = [123, "string", true]  //元素的存储,注意我们不需要担心越界的问题,当越界了,groovy会自动增加list的容量assert testList[1] == "string"assert testList[3] == null  //此时第4个元素是不存在的,可以自动增加//直接设置越界索引的元素的值testList[999] = 999//打印testList的大小println testList.size    //结果为 1000
Map
//变量的定义,使用[key:value,......]的方式定义,value可以是任何类型的对象,key可以使用''或者""包裹起来,如果不做处理则表示默认为字符串def map = [key1: "value1", "321": true, 123:"value2"]  //key1被默认为'key1'//如果key要表示特定的类型或外部定义的变量的值,则使用"()"包裹起来。def test = "999"def map1 = [(test):"1000"]//元素的存取,两种方式, map.keyName   map[keyName]println map."key1" + " " + map."123"   //结果为 value1 null, 注意使用map."123" 方式获取到的值为nullprintln map1[test] + " " + map[123]    //1000 value2,使用map[123]才能正确的获取到key值//新增/修改元素 map.keyName = valuemap."321" = falseprintln map."321"map1["test2"] = "I an test 2"println map1["test2"]
Range

Range继承自List,一种更方便的有序集合类型。

//defdef range = 10..1000  //表示从10到1000之间的元素def range2 = 10..<1000 //使用'<'表示包含最后一个元素值//打印println range.fromprintln range.get(2)println range.to

闭包

闭包其表示一段可以执行的代码块,看起来是函数与对象的结合体,闭包的最后一行是闭包的返回值,关于闭包可以参考:

  • Groovy基础——Closure(闭包)详解
  • Groovy 闭包学习笔记
闭包的基础知识
  • 闭包的定义样式
def closure = {   parm1, parm2 ->     // ->前是闭包的入参,如果没有参数,则可以直接省略这一行代码   code   ...   code                //最后一行代码,不管有没有return关键词,都表示闭包的返回值            }//exampledef testClosure = {    x, y ->    x = x + y    x}
  • 闭包的调用
//使用closure.calldef val = testClosure.call(3, 4)println val//使用closure(参数)def val2 = testClosure(5, 6)println val2
  • 当闭包没有参数时,具有一个隐含参数it,它的作用和this相似。
def itClosure = {"hello kitty $it"}//其等同于:// def itClosure = {it -> "hello kitty $it "}println itClosure('funny')//但是如果在定义闭包时,显式的使用 ->,而且前面没有任何的参数,则表示正真没有任何参数,如果调用时传入参数会报错def noParamClosure = { -> "no param"}println noParamClosure('it')//该行代码会报错
闭包在groovy中的使用
  • 闭包作为函数的最后一个参数时,在调用函数时,它的”()”号可以省略
def simpleFunction(int x, String str, Closure c){    c.call(x, str)  //在函数中调用闭包,并使用它作为返回值}def val = simpleFunction 4, " is a four", {x, str -> x + str}println val
  • 闭包作为函数的参数传入时,其闭包内部具体可使用的参数取决于该方法回调闭包时传入的参数个数和类型,也就是闭包是上下文强相关的,所以当Groovy Api 和 Gradle Api等使用闭包作为参数时,非常有必要去阅读文档,确定闭包的参数。
//还是上面的例子,使用闭包做为参数def simpleFunction(int x, String str, Closure c){    c.call(x, str)  //注意此处调用闭包时传递了两个参数,分别为x, str}//我们在调用该方法时,传入闭包参数时,注意参数的个数,类型要与函数调用时保持一致def val = simpleFunction 4, " is a four", {x, str -> x + str}  //此为正常调用//以下都会报错,groovy.lang.MissingMethodException: No signature of method: ConsoleScript3$_run_closure2.call() is applicable for argument types:(java.lang.Integer, java.lang.String) values: [5, tsttt]Possible solutions: any(), any(), doCall(调用时传入的参数类型)......def val1 = simpleFunction 5, "tsttt", {x-> x}                  def val2 = simpleFunction 5, "tsttt", {->'hahahh'} def val3 = simpleFunction 5, "tsttt", {String str, String str2 -> str1 + str2} 

Groovy综合

xxx.groovy文件编译成class文件

//在命令行执行如下命令,编译成class文件groovyc-d classes xxx.groovy

编译成class之后可以使用jd-gui工具反编译成java类进行查看。

文件I/O操作

读文件
//获取文件对象def file = new File('e:/source/groovy/test.groovy')//读取每一行并打印file.eachLine{    String strLine -> println strLine}//一次性获取所有的文件内容byte[] bytes = file.getBytes()   //注意此处可以直接使用file.bytesprintln bytes.length//使用闭包的方式用inputstream流来读取,注意它的好处不用手动去关闭输入流file.withInputStream{    is -> println(is.getText())}
写文件
//def writeFile = new File('e:/source/groovy/test2.groovy')//写入数据,重新开始写,原来的数据会被清除,若文件不存在,则自动创建 writeFile.write("this is the first line") //添加数据到最末尾writeFile.append("this is add by append")//使用流从一个文件读入写入到这个文件,原文件中的数据会别清除def readFile = new File('e:/source/groovy/test.groovy')writeFile.withOutputStream{    os ->     readFile.witInputStream{        is ->        os << is                        //重载了<< 符号,将输入流的数据传递给输出流    }}

未完待续。

5 Gradle

Gradle概述

  • Gradle是一种DSL(特定领域)脚本构建框架,编写脚本使用的是Groovy语言(也可以直接使用Java语言)。
  • 我们在脚本中配置属性时,本质是在使用GradleGroovyJava的API方法。
  • 我们使用Gradle构建项目时必须使用相应的插件:
//我们构建Android APP项目使用的插件是:apply plugin: 'com.android.application'//如果是构建一个library项目让其他的项目引用apply plugin: 'com.android.library'

所以我们在脚本中能够使用的特定的方法、属性都是各种脚本决定的。

GradleAndroid Gradle Plugin的官方帮助文档

  • Gradle releaseNote(Gradle最新版本发布说明):https://docs.gradle.org/current/release-notes
  • Gradle User Guide(最新版本用户指导手册):https://docs.gradle.org/current/userguide/userguide.html
  • Gradle DSL Reference(最新领域语言说明):https://docs.gradle.org/current/dsl/
  • Gradle Api JavaDoc(JavaDoc说明):https://docs.gradle.org/current/javadoc/
  • Goolge Gradle Plugin GitHub(Google gradle 插件GitHub地址):https://github.com/google/android-gradle-dsl
  • Gradle Android Plugin DSL Reference(最新版本插件领域语言说明):http://google.github.io/android-gradle-dsl/current/
  • Gradle Android Plugin Api JavaDoc(最新版本插件API文档):http://google.github.io/android-gradle-dsl/javadoc/current/

基本组件

  • Project,每一个项目对于Gradle都是一个Project,它可以包含一些子sub-projects或者modules,每一个project或者modules根目录下面都有一个build.gradle文件与之对应,查看project命令:
gradle projects
  • Task,gradle中的每一个task对应一个任务,所有的事务都以Task的形式存在,执行gradle脚本,实际上就是一个个task,gradle脚本被解析完成后是一个有向无循环的图,所以如果我们要自定义一些行为,需要自定义task,或者将行为依附到已有task中,如果使用自定义的task,还需要给其添加执行的依赖顺序,查看task命令:
gradle tasks//多项目查看某个项目下的tasks,或者直接cd 到想查看的项目目录下面去,执行上面的命名。gradle project_path:tasks 
  • Configurations,项目的所有配置项(Configuration)的集合,每一个Configuration都是一个文件的集合,包括dependencies(依赖项),artifacts(自定义添加的文件,如打包某些class等等),configuration可以作为Copy、Zip等task的from参数的值(参考DSL中Configuration, ConfigurationContainer, ArtifactHandler三个章节):
//拷贝complie 这个configuration的文件到指定目录中task copyAllDependencies(type: Sync) {    //referring to the 'compile' configuration    //拷贝所有编译时依赖的文件如jar,aar等等    from configurations.compile    into 'build/output'    exclude 'recyclerview-v7-24.0.0-alpha1.aar'  //过滤掉这个包}

添加自定义的artifacts到指定的Configuration中:

//Configuration的申明:configurations {    testConfig    //也可以继承    //testConfig.extendsFrom(compile)}//压缩so任务task testZip(type:Zip){    baseName = 'armeabi'    println 'ready to copy!!!!'    println(rootProject.projectDir.absolutePath + '\\app\\libs\\armeabi\\')    from rootProject.projectDir.absolutePath + '\\app\\libs\\armeabi\\'}//关联一个自定义的文件Mapdef map = [file:new File(rootProject.projectDir.absolutePath + '/app/debug.jks')]artifacts{    testConfig map    //这里也可以直接传入AbstractArchiveTask类型的task(如Jar,Zip),也可以直接传入File对象    //testConfig new File(rootProject.projectDir.absolutePath + '/app/debug.jks')    //testConfig testZip}//使用配置项,用于Copy task的from参数//copying all dependencies attached to 'compile' into a specific foldertask copyAllDependencies(type: Sync) {    //referring to the 'compile' configuration    //注意不能直接传递allArtifacts对象,要调用其getFiles()方法    from configurations.testConfig.allArtifacts.getFiles()    into 'build/output'}

Gradle执行流程

流程分析

先看一个简单的执行流程图:

Gradle执行流程图

  • 创建Gradle对象,在执行任何一条gradle命令时,都会先创建一个全局唯一的Gradle对象,该对象可以保存一些全局的基本的值。

  • 创建Setting对象,Gradle每次构建都会创建一个Setting对象,如果存在settings.gradle文件则会去解析该文件配置Setting对象。一个setting.gradle文件对应一个Setting对象,当项目依赖编译多个项目时,就需要在settings.gradle中include需要编译的与依赖的项目。后面看DSL文档会Setting对象持有一个rootProject对象,注意该对象的类型为ProjectDescriptor,而不是Project。

  • 创建Project对象,在配置完Setinng对象后,开始创建Project对象,默认的创建的顺序:先创建rootProject 对象(注意这里的类型为Project),子模块的对象的创建依照settings.gradle文件中include的顺序进行。

  • 解析上一步创建对象对应的build.gradle文件,按序添加task执行顺序,在解析完所有的Project对象后,最终会生成一个有向无环图表示所有需要执行task的执行顺序,注意解析文件是在其对应的Project对象后进行,而不是所有的Project对象创建后再进行。

  • 依据上一步生成的有向无环图,执行脚本。

DSL中对Project生命周期的描述:

Lifecycle- There is a one-to-one relationship between a Project and a build.gradle file. During build initialisation, Gradle assembles a Project object for each project which is to participate in the build, as follows:- Create a Settings instance for the build.- Evaluate the settings.gradle script, if present, against the Settings object to configure it.- Use the configured Settings object to create the hierarchy of Project instances.- Finally, evaluate each Project by executing its build.gradle file, if present, against the project. The projects are evaluated in breadth-wise order, such that a project is evaluated before its child projects. This order can be overridden by calling Project.evaluationDependsOnChildren() or by adding an explicit evaluation dependency using Project.evaluationDependsOn(java.lang.String).

流程钩子函数

在上面步骤中的3-4之间,Gradle给我们提供了一个钩子函数gradle.beforeProject,可以做一些额外的自定义操作:

//如果该钩子在setting.gradle中申明,每一个对象创建都会执行一次gradle.beforeProject{rootProject ->    println 'in xxx build.gradle beforeProject called!!!'    println rootProjct.rootDir}

同样在4-5之间也可以使用钩子函数gradle.taskGraph.whenReady,该函数在建立完有向图后执行脚本之前调用:

gradle.taskGraph.whenReady {    println 'in xxx build.gradle whenReady called!!!'    if(rootProject == project){   //若在rootProject的build.gradle中调用该方法,则会打印        println 'xxx project = rootProject'    }}

在执行完脚本之后,gradle给我们提供了一个gradle.buildFinished钩子函数供我们做一些自定义操作:

gradle.buildFinished {    println 'in xxx build.gradle buildFinished called!!!'}

注意上面的所列举的三个钩子函数传递的参数都是闭包,是可以在多个.gradle文件中调用的,可以各自实现不同的闭包逻辑,但是决定其何时生效取决于调用该方法的.gradle文件是否已经被解析,文件中调用的钩子函数是否被添加到监听器列表中,比如在rootProjectbuild.gradle中是无法监听到它自己的beforeProject钩子的,因为调用该钩子函数时,build.gradle都还没有被解析,所以我们需要在setting.gradle中声明钩子。

常用Gradle对象

Gradle对象

Gradle对象每次执行脚本时,创建的一个唯一的对象,它表示一次脚本的执行,它一直存在于脚本执行的各个阶段。以下是它属性的截图,前面说到的很多钩子函数都属于这个类。
Gradle对象属性截图

Settings对象

确定本次build需要配置Project以及执行顺序和依赖关系,多个项目关联编译时需要用到这个对象,全局唯一,在Android Studio项目通常根目录下存在一个settings.gradle文件,我们可以通过它来配置Settings对象。
Setting对象

常用Gradle命令

参考文档: Gradle命令行用户指导:https://docs.gradle.org/current/userguide/gradle_command_line.html
- 查看Gradle版本号

gradle -v
  • 执行task
gradle task1//执行多个taskgradle task1 task2//静默执行task,还有其他三种模式gradle -q task//过滤task2不执行,加-x参数gradle task1 -x task2
  • 显示所有的project
gradle projects
  • 显示所有的task
gradle tasks//显示所有
  • 显示Gradle的GUI
gradle --gui
  • 显示Gradle帮助
gradle --help

特性

0 0
原创粉丝点击