引言

Gradle是一个项目构建工具,类似Maven,可用于管理项目内部组件的依赖关系,完成自动化构建。因为Android Studio生成的Android项目默认使用gradle进行构建,因此大多数Android程序员都要跟它打交道,今天这篇文章主要想厘清Android开发中与gradle相关的一些简单但是很基础的概念。

项目自动化构建

首先想一下我们为什么需要项目的自动化构建工具,一般的开发中我们点一下Run/Debug按钮等一会apk就自动装到手机上去了,但实际上发生了很多我们看不到的事:

  • xml文件被编译成二进制文件,aidl文件编译成java,一些编译时的java代码被生成(如R文件)
  • java代码被编译成*.class字节码,最后编译成dex文件
  • 代码文件和资源文件放到一起被打包
  • 使用密钥进行安装包的签名,然后进行字节对齐的优化

以上是大致过程(细节或许有出入),其中每个过程还可以配置详细的参数(比如资源编译处理时的进程数、class编译到dex过程中进程数量、堆大小等等),如果这些小步骤都要程序员一步步去做就太繁琐了(可以看看这篇文章:手动命令行编译APK)。我们可以写一个脚本自动化地去做这些事(也就是说不用gradle这类的工具也是可以的),但是一个项目也许会引用到几十上百个库、模块之间存在复杂的依赖关系,而且项目构建还涉及到清理(clean)、测试、模块发布等环节,因此使用自动化构建工具就会方便很多。

Gradle与Wrapper

Gradle可以直接去官网下载安装,但一般没这个必要,因为Android Studio会自带一个Gradle,就在Android Studio的安装目录下。同时Gradle也支持我们在没有安装 Gradle 的机器上运行 Gradle 构建,方法就是使用Gradle wrapper。这里的Gradle wrapper可以理解为一个绿色版的便携的gradle。

在Android开发中,一个团队内可能每个人机器上的gradle版本是不一样的,版本不一样可能导致构建出现问题,因此Android Studio对于项目默认是使用gradle wrapper的,而不是使用本地gradle。每个项目根目录下的gradle目录内都有一个gradle-wrapper.properties文件,里面规定了这个项目使用的gradle版本。一般Android Studio打开加载一个新的项目时会根据这个文件的内容去下载相应版本的gradle wrapper,当然如果本地已经有了这个版本的gradle wrapper就不用下载了。对于Windows系统,下载的wrapper一般在C:/用户目录/.gradle/wrapper/dists下。

假设gradle-wrapper.properties的内容如下:

1
2
3
4
5
6
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

则说明将会去下载2.14.1的gradle wrapper,一般我们打开一个github上下载下来的项目,很容易卡死在这个环节(因为gradle.org服务器在国外,网速慢)。一般有多种解决办法:

  • 从国内站点下一个wrapper,这样网速快很多,然后拷到本地的”C:/用户目录/.gradle/wrapper/dists“下,注意命名一致。
  • 修改gradle-wrapper.properties里的版本号,找一下本地有哪些下好的版本,直接修改成这个版本,但是我是不推荐这么做,因为替换成不同版本的gradle wrapper可能导致构建失败,因为不同版本的特性可能不一样,如果只是下一个demo看看则无所谓,如果是团队协作应保证版本的一致。

基本概念

在Gradle中,比较重要的两个概念就是projectstasks。每一个构建都是由一个或多个 projects 构成的,每一个 project 是由一个或多个 tasks 构成的,一个 task 代表一些更加细化的构建, 可能是编译一些 classes, 创建一个 JAR, 生成 javadoc, 或者生成某个目录的压缩文件。

每一次Android Studio为我们自动生成的项目里的build.gradle文件里,我们可以找到:

1
2
3
task clean(type: Delete) {
delete rootProject.buildDir
}

这里就定义了叫做cleantask,我们可以在命令行里使用命令gradlew tasks来查看当前目录下的build.gradle文件里的tasks列表。我对一个新建的空项目使用了这个命令,得到如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
:tasks                                                                           

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleDebug - Assembles all Debug builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'Demo'.
components - Displays the components produced by root project 'Demo'. [incubating]
dependencies - Displays all dependencies declared in root project 'Demo'.
dependencyInsight - Displays the insight into a specific dependency in root project 'Demo'.
help - Displays a help message.
model - Displays the configuration model of root project 'Demo'. [incubating]
projects - Displays the sub-projects of root project 'Demo'.
properties - Displays the properties of root project 'Demo'.
tasks - Displays the tasks runnable from root project 'Demo' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installDebug - Installs the Debug build.
installDebugAndroidTest - Installs the android (on device) tests for the Debug build.
uninstallAll - Uninstall all applications.
uninstallDebug - Uninstalls the Debug build.
uninstallDebugAndroidTest - Uninstalls the android (on device) tests for the Debug build.
uninstallRelease - Uninstalls the Release build.

Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedDebugAndroidTest - Installs and runs the tests for debug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintDebug - Runs lint on the Debug build.
lintRelease - Runs lint on the Release build.
test - Run unit tests for all variants.
testDebugUnitTest - Run unit tests for the debug build.
testReleaseUnitTest - Run unit tests for the release build.

Other tasks
-----------
clean
extractProguardFiles
jarDebugClasses
jarReleaseClasses
transformResourcesWithMergeJavaResForDebugUnitTest
transformResourcesWithMergeJavaResForReleaseUnitTest

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task <task>

BUILD SUCCESSFUL

Total time: 14.137 secs

可以看到名为cleantask已经在Other Task分类下。让gradle执行一个task的命令是”gradle 任务名“,在Android Studio的命令行下也可以用”gradlew 任务名“(gradlew是gradle wrapper的意思)。因此对于上面列出的task清单,可以分别去跑这些task,如:

1
2
3
gradlew init
gradlew build
gradlew clean

这些task具体是干什么的就不做深究了。另外gradle/gradlew命令后面可以跟参数:比如加-q就是静默构建,不显示任何log信息,加--info就是显示info级别的log信息,加--debug就是显示debug级别的log信息。

Gradle 默认在 src/main/java 目录下查找项目源代码, 在 src/test/java 目录下查找测试代码, 因此Android Studio生成的项目的目录结构是跟gradle有关系的。(在Eclipse上开发的Android项目的目录就跟AS生成的不一样)

Gradle使用groovy语言来描述构建脚本,groovy语言跟Java比较像。这里不多介绍了。

Android Plugin

Gradle最早的版本在2007年发布,而Android手机2008年才出现,Gradle其实跟Android开发本来是没有什么关系的,直到Google开始推Android Studio后Gradle才在Android开发中慢慢使用开来。

Google推出了Android Gradle Plugin,以便在Android项目中使用gradle进行自动化构建。Android Studio生成的build.gradle自动引入了Android Gradle Plugin,在项目根目录的build.gradle中可以看到:

1
2
3
4
5
6
7
8
9
10
11
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

这里的2.2.3就是Android Plugin的版本号。如果我们要使用别的gradle插件(如android-apt),就要在这里的dependencies里添加相应的类路径(classpath),否则就可能报找不到插件之类的错误。
Google开发的Gradle插件有3种:

  • com.android.application
  • com.android.library
  • com.android.test

字面上就可以看出,分别是用于应用、库和测试模块的。应用这些插件的代码在项目的子模块下的build.gradle文件里:

1
apply plugin: 'com.android.application'

一般在引入插件后,下面就两个代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
android {
compileSdkVersion xx
buildToolsVersion "xxx"
defaultConfig {
xxx
}
buildTypes {
xxx
}
}

dependencies {
xxx
}

一般就是android{}dependencies{}。其中android代码块里面的各种配置全部源自于com.android.xxx 这个插件内,我们可以通过阅读官方文档进行相关参数的配置。而dependencies代码块内一般是对项目所需要各种库的描述。我们都知道,在GitHub上找到了一个合适的库,只要Maven仓库里有,就可以直接用一句 “compile xxx.xxx:abc:x.y.z” 代码就可以引入到项目里,项目构建时就会自动下载并添加到依赖项里。

相关配置

根据官方文档,android{}内有以下选项可以配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
aaptOptions { }
adbOptions { }
buildTypes { }
compileOptions { }
dataBinding { }
defaultConfig { }
dexOptions { }
externalNativeBuild { }
jacoco { }
lintOptions { }
packagingOptions { }
productFlavors { }
signingConfigs { }
sourceSets { }
splits { }
testOptions { }

但不是每个选项都会经常用到。经常用到的可能有:

  • signingConfigs{} 用于配置签名信息
  • splits{} 可以根据dpi、abi进行分包
  • productFlavors {} 多渠道打包
  • dexOptions{} 加速编译
    groovy语言可以使配置能加灵活和强大,不过我还在学习中,没法展开了。

参考文档

Gradle User Guide的中文翻译
android plugin的参考文档
Android项目构建官方文档

参考链接:https://www.jianshu.com/p/a54a49424f7a