简介
Gradle 是用了一种基于 Groovy 的领域特定语言(DSL,Domain Specific Language)来声明项目设置,摒弃了 XML(如 ANT 和 Maven)的各种繁琐配置。
对于 Groovy 和 Gradle 的深入理解,这里推荐一篇非常好的博客:深入理解 Android 之 Gradle
Gradle 选择了 Groovy 这门动态语言。Groovy 基于 Java 并拓展了 Java。 Java 程序员可以无缝切换到使用 Groovy 开发程序。Groovy 说白了就是把写 Java 程序变得像写脚本一样简单。写完就可以执行, Groovy 内部会将其编译成 Javaclass 然后启动虚拟机来执行。
总的来说,Gradle 基于 Groovy 实现了一套编程框架,所以我们可以在 Gradle 配置中通过编程的方式灵活的去配置我们的构建过程。Gradle 本质上是执行一系列项目构建过程,也就是所谓的自动化。为了更好地利用 Gradle,除了要知道 Gradle 如何使用之外,我们更要熟悉正在开发的项目的构建过程。
Android 项目的 .gradle 文件
在 Android Studio 新建一个项目,生成如下目录 ↓
├── app #Android App目录
│ ├── app.iml
│ ├── build #构建输出目录
│ ├── build.gradle #构建脚本
│ ├── libs #so相关库
│ ├── proguard-rules.pro #proguard混淆配置
│ └── src #源代码,资源等
├── build
│ └── intermediates
├── build.gradle #工程构建文件
├── gradle
│ └── wrapper
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell脚本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置
观察目录,会生成以下三个 .gradle 文件 ↓
接下来逐一讲解。
1. settings.gradle
用于配置 project,标明该项目有哪些 module 需要构建,如下,则代表包含一个 :app module
include ':app'
2. 工程目录下的 build.gradle
和 settings.gradle 同处于根目录下的 build.gradle 是一个顶级的 build 配置文件,在这里可以为所有 project 以及 module 配置一些常用的配置。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
// 代码托管仓库
jcenter()
}
dependencies {
// Gradle 插件及使用版本
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
// 代码托管仓库
jcenter()
}
}
// 运行gradle clean时,执行此处定义的task。
// 该任务继承自Delete,删除根目录中的build目录。
// 相当于执行Delete.delete(rootProject.buildDir)
task clean(type: Delete) {
delete rootProject.buildDir
}
可以看到代码中有两处 repositories,且闭包中都生明了 jcenter() 这个配置,其实它是一个代码托管仓库,很多开源的 Android 项目都会选择将代码托管到到 jcenter() 上,声明了这个配置,我们就可以轻松地引用 jcenter() 上任何的开源项目了。接下来,dependencies 闭包中使用 classpath 声明了一个 Gradle 插件,这是因为 Gradle 并不是专门为构建 Android 项目而开发的,Java、C++ 等很多项目都可以使用 Gradle 来构建,因此我们如果想要使用它来构建 Android 项目必须声明,其中最后的数字部分是 Gradle 插件的版本号,到这里我们工程目录下的 build.gradle 就分析完了,通常情况下,这个文件中的内容不需要修改,但是当我们模块化开发的时候,也可以在这里配置全局项目的构建相关的配置。
3. app 目录下的 build.gradle
// 表明是应用程序的插件
apply plugin: 'com.android.application'
// Android 闭包
android {
compileSdkVersion 28 // 指定项目的编译版本
defaultConfig {
applicationId "com.example.chenguiyan.justtest" // 指定项目的包名
minSdkVersion 15 // 指定项目最低兼容的安卓版本
targetSdkVersion 28 // 指定项目目标兼容的安卓版本
versionCode 1 // 指定项目的版本号
versionName "1.0" // 指定项目的版本名
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// 指定是否对项目的代码进行混淆(true:混淆 false:不混淆)
minifyEnabled false
// proguardFiles 用于指定混淆时使用的规则文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
首先第一行应用了一个插件,这里一般有两种值可选 ↓
- com.android.application: 表示是一个应用程序模块(可以直接运行)
- com.android.library: 表示是一个库模块(只能作为代码库依附于别的应用程序模块来运行)
接下来是一个 android 闭包,在这个闭包中可以配置项目构建的各种属性,其中 compileSdkVersion 用于指定项目的编译版本。
在 android 闭包中有嵌套了一个 defaultConfig 闭包,defaultConfig 闭包中可以对项目的更多细节进行配置,其中,applicationId 用于指定项目的包名,其实我们在项目创建的时候已经进行过指定,如果想后来进行修改,就可以在这里进行修改,minSdkVersion 用于指定项目的最低兼容 Android 系统版本,这里指定的是 15 表示最低兼容到 Android 4.0 系统,targetSdkVersion 指定的值表示你在该目标版本上已经做过充分的测试,系统将会为你的应用程序启用最新的功能和特性,例如 android 6.0 系统中引入了运行时权限这个功能,如果你将 targetSdkVersion 指定成 23 或更高,那么系统就会为你的程序启用运行时权限功能,而如果你将 targetSdkVersion 指定成 22,那么就说明你的程序最高只能在 Android 5.1 系统上做过充分的测试,Android 6.0 系统中引入的新功能自然就不会启用了。剩下的两个属性 versionCode 用于指定项目的版本号,versionName 用于指定项目的版本名,这两个属性在生成安装包的时候特别重要。
分析完 defaultConfig 闭包,我们接着来分析 buildTypes 闭包,buildTypes 闭包中用于指定生成安装文件的相关配置,通常会有两个子闭包,一个是 debug,一个是 release。debug 闭包用于指定生成测试版安装文件的配置,release 闭包用于指定生成正式版安装文件的配置,另外 debug 闭包是可以忽略不写的,因此我们看到上面的代码中就只有一个 release 闭包。接着我们看一下 release 闭包中的内容,minifyEnabled 用于指定是否对项目的代码进行混淆,true 表示混淆,false 表示不混淆,proguardFiles 用于指定混淆时使用的规则文件,这里可以指定两个文件,第一个 proguard-android.txt 是在 Android SDK 目录下的,里面是所有项目通用的混淆规则,第二个 proguard-rules.pro 是当前项目的根目录下的,里面可以编写当前项目特有的混淆规则,需要注意的是,通过 Android Studio 直接运行的项目生成的都是测试版安装文件。
下面还有一个 dependencies 闭包,这个闭包的功能非常强大,它主要是用来指定当前项目所有的依赖关系,通常 Android Studio 项目一共有 3 种依赖方式:本地依赖,远程依赖和库依赖。本地依赖可以对本地的 Jar 包或目录添加依赖关系,远程依赖则可以对 jcenter 库上的开源项目添加依赖关系,库依赖可以对项目中的库模块添加依赖关系。implementation fileTree 就是一个本地依赖声明,它表示将 libs 目录下所有 .jar 后缀的文件都添加项目的构建路径中,而 com.android.support:appcompat-v7:28.0.0 就是一个标准的远程依赖库格式,其中 com.android.support 是域名部分,用于和其他公司的库做区分,appcompat-v7 是组名称,用于和同一个公司中不同的库做区分,28.0.0 是版本号,用于和同一个库的不同版本做区分,加上这句声明后,Gradle 在构建项目时会首先检查一下本地是否已经有这个库的缓存,如果没有的话则会去自动联网下载,然后再添加到项目的构建路径中。而库依赖声明的基本格式是 implementation project 后面加上要依赖的库名称,另外的 testCompile 是用于声明测试用例库的。
Gradle 多项目构建
定义
前面的内容讲解只是基于创建项目后自动生成的文件,整个工程里的 module 也只有 app 这个文件夹。而现实情况里,中大型的安卓软件开发 为了提高可维护性和防止紧密耦合,可以基于特定的功能和逻辑将代码写在不同的模块中。模块通常具有层次结构而且可以定义为相互依赖。Gradle对构建多模块项目提供了强大的支持,Gradle中的每一模块都是一个项目,我们称之为多项目构建。
注意:在Android Studio中,一个模块和一个项目是有区别的。一个项目包含多个模块module。而Android Studio中的每一个module对应的是Gradle多项目构建中的一个项目。
步骤
假若我们现在要构建一个多项目工程,文件夹目录如下 ↓
rootProject
|———— settings.gradle
|———— build.gradle
|———— sub_project1
| |———— build.gradle
|———— sub_project2
| |———— build.gradle
- 在多项目构建中,根目录下的 settings.gradle 文件声明了所需的配置来实例化项目的层次结构。该脚本的执行是在构建生命周期的初始化阶段。Gradle 组装构建之前会创建一个 Settings 类型的实例。Settings 接口是 settings 文件的直接表示。若想使每个子项目都称为构建的一部分,则可以调用 Settings 接口中带有项目路径参数的 include 方法。
include ':sub_project1', ':sub_project2'
初始化阶段,gradle 会寻找到这个 settings.gradle 文件。如果该文件不存在,那么 gradle 就会假定你只有一个单独的构建模块。如果你有多个模块,且 settings.gradle 文件定义了这些模块的位置。如果这些子目录包含了其自己的 build.gradle 文件,gradle 将会运行它们,并且将他们合并到构建任务中。
- 之后在根目录下的 build.gradle 里定义子模块行为以及描述项目的一些公共插件、属性、依赖等。
方法名 | 描述 |
---|---|
allprojects | 配置当前模块以及所有子模块行为 |
subprojects | 配置所有子模块行为 |
project | 配置指定子模块行为 |
举个栗子 ↓
// 所有模块都采用统一的版本号以及groupName
allprojects {
group = 'org.pkaq.gradle.multi'
version = "0.1.0"
}
// 为所有子模块都应用java插件
subprojects {
apply plugin: 'java'
}
// 为 sub_project1 模块定义特定行为,采用war插件并且依赖base模块
project(':sub_project1') {
apply plugin: 'war'
dependencies {
compile project(':base')
}
}
- 分离配置:当项目足够复杂的时候,采用集中化配置显然不是一个好计谋。此时,将不同项目的定制行为分离到各自的脚本里无疑会显著的减轻根目录 build.gradle 的负担。这不仅可以是项目结构看起来更加清晰,脚本更加易读,分离化的配置还可以让我们更好地关注子模块的特定行为。要分离模块配置只需要很简单的两步:
- 在子模块建立 build.gradle 文件。
- 将原来的 project(':xxx'){} 内的脚本移动到上面建立的文件中去。