返回首页 深入理解 Android 之 Gradle

Groovy 介绍

Gradle 介绍

基本组件

Gradle 是一个框架,它定义一套自己的游戏规则。我们要玩转 Gradle,必须要遵守它设计的规则。下面我们来讲讲 Gradle 的基本组件:

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

一个 Project 到底包含多少个 Task,其实是由编译脚本指定的插件决定。插件是什么呢?插件就是用来定义 Task,并具体执行这些 Task 的东西。

刚才说了,Gradle 是一个框架,作为框架,它负责定义流程和规则。而具体的编译工作则是通过插件的方式来完成的。比如编译 Java 有 Java 插件,编译 Groovy 有 Groovy 插件,编译 Android APP 有 Android APP 插件,编译 Android Library 有 Android Library 插件

好了。到现在为止,你知道 Gradle 中每一个待编译的工程都是一个 Project,一个具体的编译过程是由一个一个的 Task 来定义和执行的。

一个重要的例子

下面我们来看一个实际的例子。这个例子非常有代表意义。图 22 是一个名为 posdevice 的目录。这个目录里包含 3 个 Android Library 工程,2 个 Android APP 工程。

在图 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 加载的时候,会执行 initMinshengGradleEnvironment
initMinshengGradleEnvironment()
//include 也是一个函数:  
include 'CPosSystemSdk' , 'CPosDeviceSdk' , 
      'CPosSdkDemo','CPosDeviceServerApk', 'CPosSystemSdkWizarPosImpl'

gradle 命令介绍

1.gradle projects 查看工程信息

到目前为止,我们了解了 Gradle 什么呢?

  • 每一个 Project 都必须设置一个 build.gradle 文件。至于其内容,我们留到后面再说。
  • 对于 multi-projects build,需要在根目录下也放一个 build.gradle,和一个 settings.gradle。
  • 一个 Project 是由若干 tasks 来组成的,当 gradle xxx 的时候,实际上是要求 gradle 执行 xxx 任务。这个任务就能完成具体的工作。
  • 当然,具体的工作和不同的插件有关系。编译 Java 要使用 Java 插件,编译 Android APP 需要使用 Android APP 插件。这些我们都留待后续讨论

gradle 提供一些方便命令来查看和 Project,Task 相关的信息。比如在 posdevice 中,我想看这个 multi projects 到底包含多少个子 Project:

执行 gradle projects,得到图 23:

你看,multi projects 的情况下,posdevice 这个目录对应的 build.gradle 叫 Root Project,它包含 5 个子 Project。

如果你修改 settings.gradle,使得 include 只有一个参数,则 gradle projects 的子 project 也会变少,比如图 24:

2.gradle tasks 查看任务信息

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

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

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

来看图 25:

图 25 是 gradle CPosSystemSdk:tasks 的结果。

cd CPossystemSdk

gradle tasks 得到同样的结果

CPosSystemSdk 是一个 Android Library 工程,Android Library 对应的插件定义了好多 Task。每种插件定义的 Task 都不尽相同,这就是所谓的 Domain Specific,需要我们对相关领域有比较多的了解。

这些都是后话,我们以后会详细介绍。

3.gradle task-name 执行任务

图 25 中列出了好多任务,这时候就可以通过 gradle 任务名来执行某个任务。这和 make xxx 很像。比如:

  • gradle clean 是执行清理任务,和 make clean 类似。

  • gradle properites 用来查看所有属性信息。

gradle tasks 会列出每个任务的描述,通过描述,我们大概能知道这些任务是干什么的.....。然后 gradle task-name 执行它就好。

这里要强调一点:Task 和 Task 之间往往是有关系的,这就是所谓的依赖关系。比如,assemble task 就依赖其他 task 先执行,assemble 才能完成最终的输出。

依赖关系对我们使用 gradle 有什么意义呢?

如果知道 Task 之间的依赖关系,那么开发者就可以添加一些定制化的 Task。比如我为 assemble 添加一个 SpecialTest 任务,并指定 assemble 依赖于 SpecialTest。当 assemble 执行的时候,就会先处理完它依赖的 task。自然,SpecialTest 就会得到执行了... 大家先了解这么多,等后面介绍如何写 gradle 脚本的时候,这就是调用几个函数的事情,Nothing Special!