gradle上路篇

作为写Android的人(虽然现在重心已经偏向js),当遇上gradle编译时各种各样的错误,,你却不知道怎么办,有时虽然解决了却不知道所以然,我想这样的经历绝不只有我一个。

如果说为什么对gradle始终很糊涂,那么Android Studio将要背很大的锅,因为IDE为你生成好了构建脚本,所以你无需花很多精力去了解它,你的任务更多只是在业务代码本身,事实上,学习gradle你还可以更加地了解android程序整个构建过程,在这一点上,恐怕很多人也跟当初我一样是一知半解的。

为什么是build.gradle?

对呀,为什么是这个文件?其实它没有什么特别的,只不过因为它是默认的构建脚本。所以好事者说,我想执行自己的怎么办?比如a.gradle? 好,那么其实你可以执行以下命令:

1
$ gradle -b a.gradle yourtask

这样子你可以将构建脚本替换成你想执行的a.gradle。可能对于一直点击

run](/images/run.png)图标的同学来说,`gradle`命令行甚至都没用过,这个`yourtask`是什么鬼?其实在你点击了![run
run](/images/run.png)图标的同学来说,`gradle`命令行甚至都没用过,这个`yourtask`是什么鬼?其实在你点击了![run
图标之后,gradle的构建脚本可是做了大量的工作,之后我会细说。

Groovy语言

gradle脚本其实是基于一个叫Groovy的语言编写的,它的语法相当友好,兼容java语法,有着一些java语言并没有的特性,比如函数传参。但事实上,你并不需要过多了解这门语言,对于gradle脚本来说,掌握部分就够用了。

我并不想在这里介绍Groovy语言,有闲时间我建议直接看Groovy的官方指南:

http://groovy-lang.org/documentation.html

项目示例

有些人不想看也不想了解Groovy语言,那也没事,我直接拿个例子,看完你就想去看了,拿个android工程的构建脚本,先来最简单的,用Android Studio创建完一个新的android工程之后,IDE给你准备了大致上这样的东西:

project
project

刚才不是说build.gradle是默认的吗,怎么两个啊?所以,很多人在开发时会切到真实的目录下去,像这样的:

project
project

来看看项目根目录下的build.gradle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Top-level build file where you can add configuration options common to all sub-projects/modules.
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
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

整个gradle构建脚本看上去具有拆分式JSON语法,先看最下面,这里定义了一个task,叫做cleantaskgradle内置的定义任务的关键字,不用我说你也知道clean这是干嘛用的,当你点击

project
project
这个的时候,它就在执行这个任务。

另外这个,

1
2
3
4
5
6
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
}

dependencies表示的是项目依赖,而这里定义了所引用的gradle插件, 好吧,这个'com.android.tools.build:gradle:2.2.3'它来自哪?当然是从项目存储库里取,repositories{}定义了依赖的源链接库。

注意,gradle并不是专门为android而生,根目录下的build.gradle使用的可是gradle自带的Build script blocks,
allprojects{},repositories{},dependencies{}等等(wtf? 请参阅https://docs.gradle.org/current/dsl/)。

好了,我们再继续看看app目录下的build.gradle

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
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.example.administrator.myapplication"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.0'
testCompile 'junit:junit:4.12'
}

第一行说明你引用了一个gradle插件com.android.application,就好比你在写java代码时所引用的包一样,那这个包在哪取?就是之前根目录下的build.gradle所定义的com.android.tools.build:gradle:2.2.3插件。

既然引用了插件,当然就可以开始用插件里的东西了,所以android{}标签来了,注意只有compileSdkVersionbuildToolsVersion是必须的,除此之外你可以定义一切不超出插件语法范围的配置。进一步的android gradle插件配置,可以参考http://tools.android.youdaxue.com/tech-docs/new-build-system/user-guide

1
2
3
4
5
6
7
defaultConfig {
applicationId "com.example.administrator.myapplication" //app的id
minSdkVersion 15 //最低sdk版本号
targetSdkVersion 25 //目标sdk版本号
versionCode 1 //应用版本号
versionName "1.0" //应用版本名
}

你会发现defaultConfig{}里面定义的其实都是可以在manifest.xml中可以定义的。

buildTypes代表构建类型,用于控制构建和包装应用的方式。android工程默认有两个构建类型,一个是release,一个是debug。有些人跃跃欲试了,这是不是就是多渠道打包了啊?嗯,有点接近,但并不是。插件额外定义了另外一个block,用以构建面向用户的版本,这就是productFlavors(产品风格)。

假如发布应用,一个收费版和一个免费版的,则可以这样定义:

1
2
3
4
5
6
7
8
productFlavors {
free {
applicationId "com.example.administrator.myapplication.free"
}
paid {
applicationId "com.example.administrator.myapplication.paid"
}
}

你也看到了,其实所有可以放进defaultConfig{}里的东西,都可以放到productFlavors中,如有重复项则会覆盖掉默认配置。