some question of Gradle

来源:互联网 发布:和父母关系不好知乎 编辑:程序博客网 时间:2024/05/22 13:12

Question:

怎样把任务加入到具体的编译过程中(task)?

some links:

Apk生成流程:

http://blog.csdn.net/myarrow/article/details/7965551

Gradle工作包含三个阶段:

1.首先是初始化阶段。对我们前面的multi-project build而言,就是执行settings.gradle

2.Initiliazation phase 的下一阶段是configration阶段

3.Configration阶段的目标是解析每个proje中的build.gradle。比如 muliti-project build 例子中,解析每个子目录的build.gradle.在这两个阶段之间,我们可以加一些定制化的Hook,这当然是通过Api来添加的。

4.Configuration阶段完了后,整个build的project以及内部的Task关系就确定了。恩?前面说过,一个Project包含很多的Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。所以,我们可以添加一个Hook,即当Task关系图简历好后,执行一些操作。

5.最后一个阶段就是执行任务了。当然,任务完成后,我们还可以加Hook。

关于Gradle的工作流程,你只要记住:

1.Gradle有一个初始化流程,这个时候settings.gradle会执行

2.在配置阶段,每个project都会被解析,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系

3.然后才是执行阶段。你字啊gradle xxx中指定了什么任务,gradle就会将这个xxx任务链上的所有任务全部按依赖顺序执行一遍。

Gradle基于Groovy,Groovy又基于Java,所以,Gradle执行的时候和Groovy一样,会把脚本转换成Java对象。Gradle主要有是三个对象。这三种对象和三种不同的脚本文件对应,在gradle执行的时候,会将脚本转换成相应的对象

1,Gradle对象:当我们执行gradle xxx或者什么的时候,gradle会从默认的配置脚本中构造出一个Gradle对象。在整个执行过程中,只有这么一个对象。Gradle对象的数据类型就是Gradle。我们一般很少去定制这个默认的配置脚本。

2,Project对象:每一个build.gradle会转换成一个Project对象

3,Setting对象:显然,每一个settings。gradle会转换成一个Settings对象

  注意:对于其他的gradle文件,除非定义了class,否则会转换成一个实现了Script接口的对象。

当我们执行gradle的时候,gradle首先是按顺序解析各个gradle文件。这里边就有所谓的生命周期的问题,即先解析谁,后解析谁。

  (There is a one-to-one relationship between a Project and a build.gradle file.During build initlalisation,Gradle assembles project for each project which is to participate in the build,as follows:)

1, Create a Setting instance for the build

2,Evaluate the setting.gradle script ,if present,against the Settings object to configure it

3,Use the configured Settings object to create the hierarchy of Project instance.

4,Finally,evaluate each Project by executing its build.gradle file,if present,against the project.


闭包(Closure)

def xxx = {paramters -> code}  //或者  def xxx = {无参数,纯 code}  这种 case 不需要->符号 
调用方法:

闭包对象.call(参数)

闭包对象(参数)

aClosure.call("this is string", 100)  或者  aClosure("this is string", 100)
注意:如果闭包没有定义参数的话,则隐含有一个参数,这个参数的名字叫it,和this的作用类似。it代表闭包的参数。

例如:

def greeting = { "Hello, $it!" }assert greeting('Patrick') == 'Hello, Patrick!'
等同于:

def greeting = { it -> "Hello, $it!" }assert greeting('Patrick') == 'Hello, Patrick!'
但是,如果在闭包定义时,采用下面的这种写法,则表示闭包没有参数!

def noParamClosure = { -> true }
这个时候,我们就不能给noParamClosure传参数了

noParamClosure ("test")  <==报错喔!  
Closure使用中的注意点

1.省略圆括号

闭包在Groovy中大量使用,比如很多类都定义了一些函数,这些函数最后一个参数都是一个闭包。比如:

public static <T> List<T> each(List<T> self, Closure closure)
上面这个参数表示针对List的每一个元素都会调用closure做一些处理。这里的closure做一些处理。这里的closure,就有点回调函数的感觉。但是,在使用这个each函数的时候,我们传递一个怎样的Closure进去呢?比如:

def iamList = [1,2,3,4,5]  //定义一个 ListiamList.each{  //调用它的 each,这段代码的格式看不懂了吧?each 是个函数,圆括号去哪了?        println it}

上面的代码有两个知识点:

1.each函数调用的圆括号不见了!原来,Groovy中,当函数的最后一个参数是闭包的话,可以省略园括号。比如:

def  testClosure(int a1,String b1, Closure closure){      //do something      closure() //调用闭包  }
 那么调用的时候,就可以免括号!

testClosure (4, "test", {   println "i am in closure"} )  //红色的括号可以不写..
注意:则个特点非常关键,因为以后字啊Gradle中经常会出现下图这样的代码

经常遇见上图这样没有圆括号的代码。省略圆括号虽然使得代码简洁,看起来更像脚本语言,但是它这样会让我confuse,以dolast为例,完整的代码应该按下面这种写法:

doLast({   println 'Hello world!'})
有了圆括号,你会知道doLast只是把一个Closure对象传了进去。很明显,它不代表这段脚本解析到doLast的时候就会调用println ‘Hello world’

但是把圆括号去掉后,就感觉好像 println ‘Hello world!’立即被调用一样。

2,如何确定Closure的参数

另外一个比较让人头疼的地方是,Closure的参数该怎么搞?还是刚才的each函数:

public static <T> List<T> each(List<T> self, Closure closure)
如何使用它呢?比如:

def iamList = [1,2,3,4,5]  //定义一个 List 变量  iamList.each{  //调用它的 each 函数,只要传入一个 Closure 就可以了。    println it}
看起来很轻松,其实:

对于each所需要的Closure,它的参数是什么?有多少个参数?返回值是多少?

我们能写成下面这样吗?

iamList.each{String name,int x ->  return x}  //运行的时候肯定报错!  
所以,Closure虽然很方便,但是它一定会和使用它的上下文有极强的关联。要不,作为类似回调这样的东西,我如何知道调用者传递什么参数给Closure呢?

此问题如何破解?只能通过查询API文档才能了解上下文语义。比如下图

 each函数说明中,将给指定的Closure传递Set中的每一个item。所以,Closure的参数只有一个。

findAll中,绝对抓瞎了,一个没有说明往Closure里传什么。另外没说明Closure的返回值是什么。。。

对Map的findAll而言,Closure可以有两个参数。findAll会将key和Value分别传进去。并且,Closure返回true,表示该元素是自己想要的,返回false表示该元素不是自己要找的。示意代码如图所示:

Closure的使用有点坑,很大程度上依赖于你对Api的熟悉程度,所以最初阶段,sdk查询是少不了的。

Groovy 文件I/O操作

1,读文件

Groovy中,文件读操作简单到令人发指:

def targetFile =new File(文件名)<===File对象还是要创建的。

1.读该文件中的每一行,eachLine 的唯一参数是一个Closure。Closure的参数是文件每一行的内容

其内部实现肯定是Groovy打开这个文件,然后读取文件的每一行,然后调用Closure...

targetFile.eachLine{     String oneLine ->    println oneLine     }  <==是不是令人发指??!  
2.直接得到文件内容

targetFile.getBytes()<==文件内容一次性读出,返回类型为byte[]注意前面提到的getter和setter函数,这里可以直接使用 targetFile.bytes//...

3.使用Inputstream.Inputstream的SDk

def ism =   targetFile.newInputStream()//操作 ism,最后记得关掉  
ism.close

4.使用闭包操作inputstream,以后再Gradle里会常看到这种搞法

targetFile.withInputStream{ ism ->   操作 ism. 不用 close。Groovy 会自动替你 close}
确实够简单,令人发指。withinputstream相关类的SDK地址:

java.io.File: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
java.io.OutputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html java.io.Writer:http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html

Xml

除了I/O一场简单之外,Groovy中的Xml操作也几只的很。Groovy中,XMl的解析提供了和Xpath类似的方法,名为Gpath,相关Api,请脑补 

https://en.wikipedia.org/wiki/XPath

怎么玩转Gpath

//第一步,创建 XmlSlurper 类  def xparser = new XmlSlurper()def targetFile = new File("test.xml")//轰轰的 GPath 出场  GPathResult gpathResult = xparser.parse(targetFile)//开始玩 test.xml。现在我要访问 id=4 的 book 元素。  //下面这种搞法,gpathResult 代表根元素 response。通过 e1.e2.e3 这种  //格式就能访问到各级子元素....def book4 = gpathResult.value.books.book[3]//得到 book4 的 author 元素  def author = book4.author//再来获取元素的属性和 textvalueassert author.text() == ' Manuel De Cervantes '获取属性更直观  author.@id == '4' 或者 author['@id'] == '4'属性一般是字符串,可通过 toInteger 转换成整数  author.@id.toInteger() == 4
Gradle的基本组件

Gradle中,每个待编译的工程都叫一个Project。每一个Project在构建的时候都包含一系列的Task。比如一个Android Apk的编译可能包含:Java源码编译Task,资源编译Task,Jni编译Task,lint检查Task,打包生成Apk的Task。签名Task等。

一个重要的例子:::


在图 22 的例子中:

  • CPosDeviceSdk、CPosSystemSdk、CPosSystemSdkxxxImpl 是 Android Library。其中,CPosSystemSdkxxxImpl 依赖 CPosSystemSdk
  • CPosDeviceServerApk 和 CPosSdkDemo 是 Android APP。这些 App 和 SDK 有依赖关系。CPosDeviceServerApk 依赖 CPosDeviceSdk,而 CPosSdkDemo 依赖所有的 Sdk Library。

请回答问题,在上面这个例子中,有多少个 Project?

请回答问题,在上面这个例子中,有多少个 Project?

请回答问题,在上面这个例子中,有多少个 Project?

答案是:每一个 Library 和每一个 App 都是单独的 Project。根据 Gradle 的要求,每一个 Project 在其根目录下都需要有一个 build.gradle。build.gradle 文件就是该 Project 的编译脚本,类似于 Makefile。

看起来好像很简单,但是请注意:posdevice 虽然包含 5 个独立的 Project,但是要独立编译他们的话,得:

  • cd 某个 Project 的目录。比如 cd CPosDeviceSdk
  • 然后执行 gradle xxxx(xxx 是任务的名字。对 Android 来说,assemble 这个 Task 会生成最终的产物,所以 gradle assemble)

这很麻烦啊,有 10 个独立 Project,就得重复执行 10 次这样的命令。更有甚者,所谓的独立 Project 其实有依赖关系的。比如我们这个例子。

那么,我想在 posdevice 目录下,直接执行 gradle assemble,是否能把这 5 个 Project 的东西都编译出来呢?

答案自然是可以。在 Gradle 中,这叫 Multi-Projects Build。把 posdevice 改造成支持 Gradle 的 Multi-Projects Build 很容易,需要:

  • 在 posdevice 下也添加一个 build.gradle。这个 build.gradle 一般干得活是:配置其他子 Project 的。比如为子 Project 添加一些属性。这个 build.gradle 有没有都无所属。
  • 在 posdevice 下添加一个名为 settings.gradle。这个文件很重要,名字必须是 settings.gradle。它里边用来告诉 Gradle,这个 multiprojects 包含多少个子 Project。

来看 settings.gradle 的内容,最关键的内容就是告诉 Gradle 这个 multiprojects 包含哪些子 projects:

[settings.gradle]//通过 include 函数,将子 Project 的名字(其文件夹名)包含进来  include  'CPosSystemSdk' , 'CPosDeviceSdk' ,       'CPosSdkDemo','CPosDeviceServerApk', 'CPosSystemSdkWizarPosImpl'

强烈建议:

如果你确实只有一个 Project 需要编译,我也建议你在目录下添加一个 settings.gradle。我们团队内部的所有单个 Project 都已经改成支持 Multiple-Project Build 了。改得方法就是添加 settings.gradle,然后 include 对应的 project 名字。

另外,settings.gradle 除了可以 include 外,还可以设置一些函数。这些函数会在 gradle 构建整个工程任务的时候执行,所以,可以在 settings 做一些初始化的工作。比如:我的 settings.gradle 的内容:

//定义一个名为 initMinshengGradleEnvironment 的函数。该函数内部完成一些初始化操作  //比如创建特定的目录,设置特定的参数等  def initMinshengGradleEnvironment(){    println "initialize Minsheng Gradle Environment ....."    ......//干一些 special 的私活....    println "initialize Minsheng Gradle Environment completes..."}//settings.gradle 加载的时候,会执行 initMinshengGradleEnvironmentinitMinshengGradleEnvironment()//include 也是一个函数:  include 'CPosSystemSdk' , 'CPosDeviceSdk' ,       'CPosSdkDemo','CPosDeviceServerApk', 'CPosSystemSdkWizarPosImpl'

gradle tasks 查看任务信息

查看了 Project 信息,这个还比较简单,直接看 settings.gradle 也知道。那么 Project 包含哪些 Task 信息,怎么看呢?图 23,24 中最后的输出也告诉你了,想看某个 Project 包含哪些 Task 信息,只要执行:

gradle project-path:tasks 就行。注意,project-path 是目录名,后面必须跟冒号。

对于 Multi-project,在根目录中,需要指定你想看哪个 poject 的任务。不过你要是已经 cd 到某个 Project 的目录了,则不需指定 Project-path。


Gradle的工作流程

Gradle的工作流程其实蛮简单,用一个图26来表达


图26告诉我们,Gradle工作包含三个阶段:

1,首先是初始化阶段。对我们前面的multi-project build而言,就是执行settings.gradle;

2,Initliazation phase的下一个阶段是Configration阶段。

3,Configration阶段的目标是解析每个project中的build.gradle。比如multi-project build的例子中,解析每个子目录中的build.gradle。在这两个阶段之间,我们可以加一些定制化的Hook。这当然是通过APi来添加的。

4,Configuration阶段完成了后,整个build的project以及内部的Task关系就确定了。恩?前面说过,一个Project包含对个Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。所以,我们可以添加一个Hook,即当Task关系图建立好后,执行一些操作。

5,最后一个阶段就是执行任务了,当然,任务完成后,我们还有可以加Hook。

最后,关于 Gradle 的工作流程,你只要记住:

  • Gradle 有一个初始化流程,这个时候 settings.gradle 会执行。
  • 在配置阶段,每个 Project 都会被解析,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系。
  • 然后才是执行阶段。你在 gradle xxx 中指定什么任务,gradle 就会将这个 xxx 任务链上的所有任务全部按依赖顺序执行一遍!

0 0
原创粉丝点击