Gradle系列(二):Gradle初步

Gradle简介

Gradle是一个构建工具,同时它也是一个编程框架。

  • 当你把Gradle当构建工具看的时候,我们只想着如何用好它。会写、写好配置脚本就OK。

构建,叫build也好,叫make也行。反正就是根据输入信息然后干一堆事情,最后得到几个产出物(Artifact)。
最最简单的构建工具就是make了。make就是根据Makefile文件中写的规则,执行对应的命令,然后得到目标产物。
日常生活中,和构建最类似的一个场景就是做菜。输入各种食材,然后按固定的工序,最后得到一盘菜。当然,做同样一道菜,由于需求不同,做出来的东西也不尽相同。比如,宫保鸡丁这道菜,回民要求不能放大油、口淡的要求少放盐和各种油、辣不怕的男女汉子们可以要求多放辣子....总之,做菜包含固定的工序,但是对于不同条件或需求,需要做不同的处理。
在Gradle爆红之前,常用的构建工具是ANT,然后又进化到Maven。ANT和Maven这两个工具其实也还算方便,现在还有很多地方在使用。但是二者都有一些缺点,所以让更懒得人觉得不是那么方便。比如,Maven编译规则是用XML来编写的。XML虽然通俗易懂,但是很难在xml中描述if{某条件成立,编译某文件}/else{编译其他文件}这样有不同条件的任务。
Gradle中,每一个待编译的工程都叫一个Project。
每一个Project在构建的时候都包含一系列的Task。比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等

  • 当你把Gradle当做编程框架看的时候,你可能需要学习很多更深入的内容。

Gradle作为框架,负责定义流程和规则。而具体的编译工作则是通过插件(Plugin)的方式来完成的,插件就是用来定义Task,并具体执行这些Task的东西。
比如编译Java有Java插件,编译Groovy有Groovy插件,编译Android APP有Android APP插件,编译Android Library有Android Library插件

Gradle编程框架的问题放在最后讲解,在此之前,先讲以下内容:

Android Plugin for Gradle—— 应用于Gradle的Android插件

Reference:
https://developer.android.com/studio/releases/gradle-plugin.html?hl=zh-cn#updating-plugin
首先,根据以上链接中对Android插件的介绍,做一个简要的翻译:

Android Studio的构建系统基于Gradle,同时,应�用于Gradle的Android插件又专门为构建Android App增加了若干特性。虽然Android插件与Android Studio保持同步更新,但是该插件依然能够独立于Android Studio使用,且可独立更新。

对于Android项目,在项目的根目录的build.gradle 文件(即,Application级别的build.gradle文件),里面有这么一句代码:

classpath 'com.android.tools.build:gradle:2.2.0'

这行代码就表示,该Android项目构建过程本身需要依赖Android Plugin for Gradle,后面的版本号代表就是需依赖该插件的2.2.0这个版本。
注意,不是 Gradle 的版本哟,这个插件是 Google 搞的,跟 Gradle 官方没关系。

Gradle Wrapper

  • Google 推出的 Gradle Wrapper ,它会在你的每个项目中都配置一个指定版本的 Gradle,你可以理解为每个 Android 项目本地都有一个小型的 Gradle ,通过Gradle Wrapper来支持每个项目可以用不同的 Gradle 版本来进行构建。

  • Gradle is a tool that is under constant development, and new versions could potentially break backward compatibility.

  • Using the Gradle Wrapper is a good way to avoid issues and to make sure builds are reproducible.

  • The Gradle Wrapper provides a batch file on Microsoft Windows and a shell script on other operating systems. When you run the script, the required version of Gradle is downloaded (if it is not present yet) and used automatically for the build.

  • The idea behind this is that every developer or automated system that needs to build the app can just run the wrapper, which will then take care of the rest.This way, it is not required to manually install the correct version of Gradle on a developer machine or build server.

  • Therefore, it is also recommended to add the wrapper files to your version control system.

  • 输出Gradle版本号和其他信息。

./gradlew -v
./gradlew -v

tasks

  • 列出所有可用的tasks
    This will print out a list of all the available tasks. If you add the --all parameter, you will get a more detailed overview with the dependencies for every task.
./gradlew tasks
./gradlew tasks
  • assemble task with the debug configuration
    This task will create an APK with the debug version of the app. By default, the Android plugin for Gradle saves the APK in the directory MyApp/app/build/ outputs/apk.
./gradlew assembleDebug

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

settings.gradle

settings.gradle中的内容,最关键的就是告诉Gradle这个multi-projects包含哪些子projects。
如果你确实只有一个Project需要编译,也建议你在目录下添加一个settings.gradle,然后include对应的project名字。
另外,settings.gradle除了可以include外,还可以设置一些函数。这些函数会在gradle构建整个工程任务的时候执行,所以,可以在settings做一些初始化的工作。比如:

//定义一个名为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' 

The settings file is executed during the initialization phase, and defines which modules should be included in the build. In this example, the app module is included. Single module projects do not necessarily require a settings file, but multi-module projects do; otherwise, Gradle does not know which modules to include.

build.gradle

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

  • The top-level build.gradle file is where you can configure options that need to be applied to all the modules in the project. It contains two blocks by default:
buildscript { 
  repositories {
    jcenter()
  }
  dependencies { 
    classpath 'com.android.tools.build:gradle:1.2.3'
  }
}
allprojects { 
  repositories {
     jcenter()
  }
}

基本的Gradle命令

gradle projects #查看工程信息,比如想看一个multi-projects到底包含多少个子Project,跟settings.gradle中include的project有关
 gradle tasks #查看任务信息
gradle task-name #执行名为task-name的任务,
#比如:gradle clean执行清理任务,gradle properites查看所有属性信息

先顺便写这么几个吧,之后再不断补充....

构建的生命周期(Build Lifecycle)

执行Gradle build,就是在task上执行各种actions,而一个task可能又依赖其他task,task的执行流程是有向非循环图
Gradle build有三个阶段:

  1. 初始化阶段 Initialization
    Gradle有一个初始化流程,这个时候settings.gradle会执行。
    This is where the Project instance is created. If there are multiple modules, each with their own build.gradle file, multiple projects will be created.
  2. 配置阶段 Configuration
    在配置阶段,每个Project都会被解析,其内部的task也会被添加到一个有向图里,用于解决执行过程中的依赖关系。
    Configuration阶段完了后,整个build的project以及内部的Task关系就确定了。
    一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。
    In this phase, the build scripts are executed, creating and configuring all the tasks for every project object.
  3. 执行阶段 Execution
    你在gradle xxx中指定了xxx任务,gradle就会将这个xxx任务链上的所有任务全部按依赖顺序执行一遍。
    This is the phase where Gradle determines which tasks should be executed. Which tasks should be executed depends on the arguments passed for starting the build and what the current directory is.

好,接下来将Gradle作为一种编程框架来讲解:

Gradle框架与常用API

Gradle基于Groovy,Groovy又基于Java。所以,Gradle执行的时候和Groovy一样,会把脚本转换成Java对象。

基本

Gradle主要有三种对象,这三种对象和三种不同的脚本文件对应,在gradle执行的时候,会将脚本转换成对应的对象:

Type of script Delegates to instance of
init script Gradle
build script Project
settings script Settings
  • Gradle对象:当我们执行gradle xxx的时候,gradle会从默认的配置脚本中构造出一个Gradle对象。在整个执行过程中,只有这么一个对象。Gradle对象的数据类型就是Gradle。我们一般很少去定制这个默认的配置脚本。
  • Project对象:每一个build.gradle会转换成一个Project对象。
    apply是一个函数。
    注意,Groovy支持函数调用的时候通过
参数名1:参数值2, 参数名2:参数值2 

的方式来传递参数,比如:

apply plugin: 'com.android.library'    #如果是编译Library,则加载此插件
apply plugin: 'com.android.application'  #如果是编译Android APP,则加载此插件

除了加载二进制的插件(上面的插件其实都是下载了对应的jar包,这也是通常意义上我们所理解的插件),还可以加载一个gradle文件。为什么要加载gradle文件呢?

  • Settings对象:每一个settings.gradle都会转换成一个Settings对象。
  • Script:除了以上三个对象之外,每个Gradle脚本都实现Script接口。

build script

A build script is made up of zero or more statements and script blocks.
Statements can include method calls, property assignments, and local variable definitions.
A script block is a method call which takes a closure as a parameter. The closure is treated as a configuration closure which configures some delegate object as it executes.
The top level script blocks are listed below.

allprojects { }

Configures this project and each of its sub-projects.

artifacts { }

Configures the published artifacts for this project.

buildscript { }

Configures the build script classpath for this project.

configurations { }

Configures the dependency configurations for this project.

dependencies { }

Configures the dependencies for this project.

repositories { }

Configures the repositories for this project.

sourceSets { }

Configures the source sets of this project.

subprojects { }

Configures the sub-projects of this project.

publishing { }

Configures the PublishingExtension
added by the publishing plugin.

举个栗子

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {    //为整个Project配置build script的classpath,旭神:项目编译需要的
  repositories {       
    jcenter()   
  }    
  dependencies {        
    classpath 'com.android.tools.build:gradle:2.2.0'     //gradle plugin的版本  
      // NOTE: Do not place your application dependencies here; they belong        
     // in the individual module build.gradle files    
  }
}

allprojects {   //Configures this project and each of its sub-projects. 
  repositories {        
    jcenter()    
  }
}

task clean(type: Delete) {    //创建名为clean的task,并且这个clean task是从Delete这个task派生出来的
  delete rootProject.buildDir
}

在以上栗子中,关于task做几点说明:
1.Task是Gradle中的一种数据类型,它代表了一些要执行或者要干的工作。不同的插件可以添加不同的Task。每一个Task都需要和一个Project关联。
2.Task的API文档位于:
https://docs.gradle.org/current/dsl/org.gradle.api.Task.html
3.语法方面:利用Project的task()函数新建一个task,这个task的名字叫clean,关于task的创建,有如下几种形式:

//Task是和Project关联的,所以,我们要利用Project的task函数来创建一个Task  
task myTask  //myTask是新建Task的名字  
task myTask { configure closure }  
task myType << { task action }  //注意,<<符号是doLast的缩写 
task myTask(type: SomeType)  
task myTask(type: SomeType) { configure closure } 

在上面几种形式中:

  • 一个Task包含若干Action。所以,Task有doFirst和doLast两个函数,用于添加需要最先执行的Action,和需要最后执行的Action。Action就是一个闭包。
  • Task创建的时候可以指定Type,通过type:名字表达。这是什么意思呢?其实就是告诉Gradle,这个新建的Task对象会从哪个基类Task派生。比如,Gradle本身提供了一些通用的Task,�比如Delete,Delete是Gradle中的一个类。当我们:task myTask(type:Delete)的时候,创建的Task就是一个Delete Task。
  • 当我们使用 task myTask{xxx}的时候。花括号是一个closure。这会导致gradle在创建这个Task之后,返回给用户之前,会先执行closure的内容。
  • 当我们使用task myTask << {xxx}的时候,我们创建了一个Task对象,同时把closure做为一个action加到这个Task的action队列中,并且告诉它“最后才执行这个closure”(注意,<<符号是doLast的代表)。

推荐阅读更多精彩内容