1.1新构建系统的特性如果你正在查阅build.gradle文件的所有可选项,请点击这里进行查阅:DSL参考
gradle构建系统具有如下的特点:
- 易于代码和资源复用
- 易于创建应用的版本,例如发布多apk以及应用的不同渠道版本
- 构建过程易于配置,扩展和优化
- 良好的IDE整合
Gradle既是一个先进的构建系统,也是一个允许通过插件创建自定义构建逻辑的构建工具集。以下是一些我们为什么选择Gradle的原因:
- 用于描述和操作构建逻辑的,基于Groovy的特定领域语言(DSL)
- 基于Groovy的构建文件,允许混合通过使用DSL的声明式元素以及使用代码去操作DSL元素来提供自定义逻辑。
- 通过Maven或者Ivy的内置依赖管理
- 非常灵活。
- 插件能够导出自己的DSL以及自己的API,用于使用构建文件
- 支持IDE整合的良好工具API
- Gradle 2.2
- SDK版本19.0.0及以上,一些特性可能会需要更新的版本
一个Gradle项目在一个文件中描述了该项目的构建情况,该文件被称为build.gradle,位于项目的根目录。(点击这里查看构建系统概述)
2.1简单的构建文件大多数简单的Android项目拥有如下的build.gradle文件:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.1.0"
}
在Android的构建文件中,有以下三个主要区域:
buildscript{…}
用于配置用于构建的代码。在上面的例子中,声明使用jCenter库,并且有一个依赖于Maven类路径的Maven Artifact。该Artifact是包含包含Android构建插件的1.3.1版本Gradle。
上例中的Artifact仅仅影响到构建代码的运行,并不作用于代码项目。至于项目本身需要声明自己的库和依赖关系,该部分将会在下文介绍。
之后,该构建文件应用了com.android.application插件。该插件用于构建Android应用。
最后android{…}
中配置了所有用于Android应用构建的参数。这里是Android特定领域语言(DSL)的入口点。默认情况下,仅仅编译目标(compilation target)和构建工具版本(version of the build-tool)是必备的。也就是上例中的compileSdkVersion属性和buildtoolsVersion属性。该编译目标和旧版本的project.properties文件中的target属性是相同的。新版本中既可以指定一个整型(即API level),也可以使用表示相同值的字符串对compileSdkVersion进行指定。
2.2项目结构重点:你应当使用com.android.application插件,使用java插件会导致错误。
注意1:另外,你也需要一个local.properties文件,设置sdk.dir去配置本地的SDK路径位置。
注意2:另外,你也可以设置名为
ANDROID_HOME
的环境变量。 这两者并没有什么区别,你可以选择任意一种。例如:sdk.dir=/path/to/Android/Sdk
上面例子的基本构建文件预期了一个默认的文件夹结构。Gradle遵循“约定优于配置”的概念,尽可能提供良好的默认项值。基本项目由两个被称为代码集(source sets)的部分构成,一个是主要的源代码,另一个是测试代码。它们各自位于:
src/main
src/androidTest
在这些目录里,每一个资源组件都有各自的子目录。对于Java和Android插件,java源代码和java资源的路径位置为:
java/
resources/
对于Android插件而言,有如下特定的文件和目录:
AndroidManifest.xml
res/
assets
aidl/
rs/
jni/
jniLibs/
这意味着主资源集中的所有*.java
文件位于src/main.java
中,主清单文件(main manifest)位于src/main/AndroidManifest.xml
。
注意:
src/androidTest/AndroidManifest.xml
由于会自动创建,因此并不是必须手动编写的。
2.2.1配置结构
当默认的项目结构并不完善时,可能需要进行配置。本部分只介绍Android项目结构的配置,关于纯java项目的项目结构配置,请参阅:gradle documentation。
Android插件使用和纯java项目相同的语法,但是由于其使用自己的资源集,项目的配置由android{...}
代码块完成。例如,旧的项目结构(Eclipse)中对主代码和测试代码进行映射:
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
androidTest.setRoot('tests')
}
}
2.3构建的任务注1: 因为旧的结构中在同一个文件夹中放入所有的资源文件,我们需要对这些资源集重新进行映射,将java代码,资源文件等等放入src文件夹。
注2:setRoot()
移动整个资源集和其子文件夹到一个新的文件夹中,上例将src/androidTest/*
移动到tests/*
中,当然此处的语句androidTest.setRoot('tests')
只是Android的特性,并不适用于Java的资源集。
2.3.1通用任务
在构建文件中应用插件会自动创建一系列构建任务的集合。Java插件和Android插件都是如此。关于任务的约定如下:
- assemble 该任务用于组合项目的输出
- check 该任务用于运行所有的的检测
- build 该任务执行assemble和check任务
- clean该任务清除项目的输出
任务assemble,check以及build并不真正做任何事。这些插件中的“祖先”任务用于添加在任务中真正执行的任务。
这样,不论当前的任务类型是什么以及何种插件被应用,都将会允许你使用执行相同的任务。例如,findbugs插件将会创建一个一个新的任务,并通过check任务进行依赖,无论什么时候执行调用该任务,都可以直接使用check任务。
在命令行中,你可以通过下面的命令得到高级别任务:
gradle tasks
查看所有依赖任务列表可以使用下面的命令:
gradle tasks —all
注意:gradle自动显示任务已经声明任务的输入和输出情况。
当你在没有变更项目内容的情况下再次运行构建任务时,Gradle将会报告所有的任务已经UP-TO-DATE,意味着没有需要运行的任务。这样会使得任务彼此正确的依赖,而不需要没必要的构建操作。
2.3.2Java项目任务
以下是由Java插件所创建的,依赖于祖先任务的最重要的两个任务:
assemble
jar:创建输出的任务check
test:运行测试的任务
jar任务本身直接或间接依赖于其他任务:例如classes任务将会编译Java代码。测试代码被任务testClasses所编译,但是就像classes任务一样,几乎很少有人会去调用,因为执行test任务即可。
总的来说,你可能应当仅仅调用assemble任务或者check任务并忽略其他的任务。你可以点击这里查看Java插件所有的任务集合及其描述信息:Java插件的任务集合
2.3.3Android的任务
Android插件使用相同的概念来保持和其他插件之间的兼容性,并且,Android插件还添加了额外的祖先任务:
- assemble
- check
- connectedCheck 运行check任务需要一个连接的设备或者模拟器,在所有的连接设备中,该任务将并行执行。
- deviceCheck 使用API去连接远程设备,用于CI服务器(持续集成服务器)。
- build
- clean
新的祖先任务是必备的,这是为了能够在不需要连接设备的情况下进行规则检查。请注意,build任务并不依赖deviceCheck任务或者connectedCheck任务。
一个Android的项目有至少两个输出文件:一个debug apk文件以及一个release apk文件。它们中的每一个都有其自己的祖先任务来优化构建:
- assemble
- assembleDebug
- assembleRelease
assembleDebug和assembleRelease两者都依赖于其他的多步任务执行来构建app文件。assemble任务依赖于assembleDebug和assembleRelease,可调用assemble任务构建上述两种apk。
提示: gradle支持骆驼命名法缩写的形式在命令行中为任务命名。例如:
gradle aR
和下面的命令相同:
gradle assembleRelease
除非有其他任务和’aR’重复。
check任务有自己的依赖项:
check
lint
connectedCheck
connectedAndroidTest
deviceCheck
- 这取决于当任务创建时,其他插件什么时候实现测试拓展点。
最后,插件创建了安装了卸载所有构建类型的任务(包括debug,release和test),只要能够被安装(需要签名)。例如:
installDebug
installRelease
uninstallAll
uninstallDebug
uninstallRelease
uninstallDebugAndroidTest
Android插件提供了一个宽泛的领域定制语言(DSL)在构建系统中对大多数内容进行自定义。
2.4.1Manifest条目
通过DSL,能够配置最重要的manifest条目,例如:
minSdkVersion
targetSdkVersion
versionCode
versionName
applicationId
关于包名,详情请点击:应用ID VS 包名testApplicationId
(用于测试apk)testInstrumentationRunner
例如:
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
versionCode 12
versionName "2.0"
minSdkVersion 16
targetSdkVersion 23
}
}
关于完整的构建属性清单以及其默认值,请查看Android插件特定领域语言参考。
把这些清单属性放入构建文件中的好处是,这些值可以动态获取。例如,别人可以从文件中阅读版本名称或者使用自定义逻辑:
def computeVersionName() {
//...
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
versionCode 12
versionName computeVersionName()
minSdkVersion 16
targetSdkVersion 23
}
}
注意:不要使用当前域中可能和getters冲突的文件名。例如
defaultConfig{...}
调用getVersionName()
会自动使用defaultConfig.getVersion()
而不是自定义的方法。
2.4.2构建类型
默认情况下,Android插件自动建立了debug版和release版应用。二者最大的不同是在安全设备(非开发设备)上的调试能力,以及APK文件被签名的详情信息。debug版本是由已知用户名/密码(防止在构建过程中的提示)所自动创建的key/证书所签名。release版本在构建过程中并不被签名,这将会在后面发生。
这项配置通过一个叫做BuildType
的对象完成。默认情况下,有两个实例被创建,分别是debug版和release版。Android插件运行自定义这两个实例,就像其他构建类型一样。这是由buildTypes
特定领域语言容器所完成的:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug {
initWith(buildTypes.debug)
applicationIdSuffix ".jnidebug"
jniDebuggable true
}
}
}
上述片断实现了如下内容:
- 配置默认的debug构建类型
- 设置包为”应用ID.debug”,使得应用的debug版和release版都能在同一个设备上安装。
- 创建了一个新的叫作
jnidebug
的构建类型,并对其使用debug
构建类型进行复制。 - 继续配置
jnidebug
,开启JNI组件调试功能,并添加一个不同的包名后缀。
创建一个新的构建类型就和使用buildTypes
容器下的一个新元素一样简单,要么调用initWith()
要么将其完全配置结束。关于构建类型的完整属性清单,请查阅:Android构建类型参考
另外,关于修改构建属性,构建类型可以用于添加指定的代码和资源文件。对每一种构建类型,新的相匹配资源集被创建,其默认位置为”src/构建类型名称”,例如src/debug/java
目录能够用于添加仅仅在debug APK文件中所编译的代码或资源文件。这意味着构建类型的命名不能和main
以及androidTest
重复(,并且必须独一无二(这是插件所限制的)。
就像其他任何的资源集一样,构建类型资源的位置能够重新指定:
android {
sourceSets.jnidebug.setRoot('foo/jnidebug')
}
此外,对于每一种构建类型,一个新的assemble构建类型名称
任务被创建,例如assembleDebug
。assembleDebug
任务和assembleRelease
任务在上文中已经被提及,这也就是他们为什么会存在的原因。当debug
构建类型和release
构建类型预创建时,这些任务(assembleDebug
和assembleRelease
)也会被自动创建。根据这个规则,上述的build.gradle片段也将会生成一个叫做assembleJnidebug
的任务,该任务的依赖关系也和assembleDebug
以及assembleRelease
一样。
提示:请记得你能够输入
aJ
来运行assembleJnidebug
任务。
可能的使用情况:
- 一些权限只在debug模式下开启,在release模式下禁用
- 调试的自定义实现
- debug模式下的使用资源不同(例如当一个资源值与资源证书相挂钩时)
不同构建类型的代码/资源被用于以下情况: - Manifest清单被合并到app清单文件中
- 作为其他资源文件夹的代码
- 资源文件被主资源文件覆盖并替换现有的值
2.4.3签名配置
对一个应用的签名需要以下内容(关于APK文件签名的详细信息,请查阅签名你的应用):
- keystore
- keystore密码
- keystore别名
- key密码
- 商店类型
位置,key名称以及密码和商店类型共同构成签名配置。默认情况下,debug
配置使用debug keystore,其带有已知的密码和默认的key和key的密码。debug的keystore位于$HOME/.android/debug.keystore
,如果不存在会被创建。debug
构建类型被自动设成使用debug
签名配置。
创建其他配置信息或自定义默认的内置配置信息是可行的。这是通过signingConfigs
特定领域语言容器完成的:
android {
signingConfigs {
debug {
storeFile file("debug.keystore")
}
myConfig {
storeFile file("other.keystore")
storePassword "android" keyAlias "androiddebugkey"
keyPassword "android"
}
}
buildTypes {
foo {
signingConfig signingConfigs.myConfig
}
}
}
上述片段改变了debug版本keystore文件的位置为项目的根目录。这将会自动影响到任何构建类型,任何构建类型都能够使用它。在上例中即为debug
构建类型。这也将会创建一个新的签名配置,新的构建类型(foot
)便可以使用这个新的签名配置。
3.依赖,Android库以及多项目建立 3.1依赖二进制包注意1:只有debug keystore文件自动创建并位于默认位置。改变debug keystore文件位置并不会按需创建。只有使用不同命名创建签名配置并使用默认的debug keystore位置的情况下才会自动创建。从另一方面来说,这是和keystore文件的位置挂钩的,而不是和配置信息的命名相对应的。
注意2:keystore文件的位置通常情况下和项目的根目录相关,但是也能够是绝对目录,虽然这并不被推荐(除非是debug的,因为它会自动被创建)。
注意3:如果你把这些文件纳入到版本控制系统中,你可能并不想要把密码放在其中。这里介绍了从控制台或环境变量中读取值的方法。
3.1.1本地包
要去配置一个依赖库或者额外的jar库,你需要在compile
配置中添加一个依赖关系。下面的片段在libs文件夹中添加了所有jar文件的依赖关系:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
android {
//...
}
注意:
dependencies
特定领域语言元素是标准Gradle API中的一部分。在这里面的每一项都被加入到编译类路径中,并都在最终的APK文件中被打包进去。以下是可能的配置信息:
compile
主应用androidTestCompile
测试应用debugCompile
debug构建类型releaseCompile
release构建类型
由于在构建APK时并不可能没有关联的构建类型,因此APK总是被配置至少两个编译配置信息:compile
以及构建类型Compile
。创建一个新的构建类型将会自动创建一个基于该构建类型名称的编译配置。如果debug版本需要添加一个自定义的库(如报告程序崩溃情况)时,或者不同构建类型依赖于相同库的不同版本时这将是非常有用的(点击这里详见不同版本的冲突是如何处理的)。
3.1.2远程依赖
Gradle支持从Maven和Ivy库中拉取依赖(artifact)。首先,该仓库必须被添加到列表中,其次依赖必须被声明。
repositories {
jcenter()
}
dependencies {
compile 'com.google.guava:guava:18.0'
}
android {
//...
}
注意1:
jcenter()
是指定仓库的URL缩写。Gradle同时支持远程和本地的仓库。注意2:Gradle遵循依赖的传递性。这意味着,如果如果一个依赖,依赖于其本身,这也会被拉取。
关于建立依赖的更多信息,请阅读Gradle使用指南和DSL文档
3.2多项目的搭建Gradle项目也能够通过多项目搭建依赖于其他Gradle项目。一个多项目的搭建通常为将所有项目作为已给项目根目录的子目录。例如,给出如下的结构:
MyProject/
+ app/
+ libraries/
+ lib1/
+ lib2/
我们能够识别出三个项目。Gradle会用下述的命名进行参考:
:app
:libraries:lib1
:libraries:lib2
每一个项目拥有其自己的build.gradle文件,声明了其如何得到其构建。额外的,将会有一个被命名为setting.gradle的文件位于根目录,用于声明所有项目。一下给出了结构:
MyProject/
| settings.gradle
+ app/
| build.gradle
+ libraries/
+ lib1/
| build.gradle
+ lib2/
| build.gradle
setting.gradle的内容非常简单。它定义了哪个文件夹是一个Gradle项目:
include ':app', ':libraries:lib1', ':libraries:lib2'
:app
项目很可能依赖于其他的项目作为库,这是通过以下声明实现的:
dependencies {
compile project(':libraries:lib1')
}
关于多项目搭建的更多信息,请点击这里。
3.3库项目在上述的多项目搭建中:libraries:lib1
和 :libraries:lib2
作为Java项目,:app
Android项目将会使用这两个项目的jar输出。但是,如果你想要通过Android API或使用Android风格的资源来共享代码,这些库就不能是常规的Java项目,它们必须是Android库项目。
3.3.1创建一个库项目
一个库项目和一个常规的Android项目非常相似。因为构建库和构建应用是不同的,因此使用的插件也不同。本质上来说,两个插件大多数代码都是相似的,并且都由相同的com.android.tools.build.gradle
jar文件提供。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
}
这将会创建一个使用API 23进行编译的库项目。资源集,构建类型以及依赖关系的使用都和应用项目相同,并可以通过相同的方式自定义。
3.3.2项目和项目库的区别
库项目的主要输出是一个.arr包(表示Android archive),是编译代码(就像jar文件或者原生的.so文件一样)和资源文件(清单,res文件以及assets文件)的组合。一个库项目也可以生成一个测试apk文件用于测试库的独立性。对此使用的祖先任务也是相同的(assembleDebug
和assembleReleas
)。因此用命令去构建这样一个项目并没有什么区别。对于其他方面,库项目表现得和应用项目一样。库项目也拥有构建类型和产品渠道(product flavors,见下文),并且能够潜在地生成多个版本的aar文件。请注意,构建类型的大多数配置并不能够用于库项目。但是你可以使用自定义资源集来改变库的内容,这取决于当前库是否被用于一个项目,或者用于被测试。
3.3.4引用一个库
引用一个库和二进制包被引用是一样的:
dependencies {
compile project(':libraries:lib1')
compile project(':libraries:lib2')
}
注意:如果你有多个库,那么引用的顺序就变得很重要。这和旧的构建系统中在project.properties文件中的依赖顺序一样重要。
3.3.5库发布
默认情况下,一个库仅仅发布其release版本。该版本被用于所有项目对库的引用,不论这些项目本身使用怎样的的构建类型,而这是一个我们将要取消的临时限制。你可以控制要发布何种版本:
android {
defaultPublishConfig "debug"
}
注意,发布的配置名参考于版本的命名。Release和Debug版仅仅适合没有渠道的情况下。如果你想要使用渠道改变默认的发布版本,你可以这样写:
android {
defaultPublishConfig "flavor1Debug"
}
发布一个库的所有版本是可能的。我们计划允许使用一个标准的项目到项目的依赖,但是目前这是不可能的,这是因为Gradle中的一些限制(我们正在努力解决这些)。
发布所有版本在默认情况下是不可以的。下面的片段展示了如何开启这项功能:
android {
publishNonDefault true
}
很重要的一点是,你需要意识到发布多版本的arr文件而不是包含多个版本的arr文件。每一个arr包包含单个版本。发布一个版本意味着使得这个arr文件和Gradle项目输出的依赖一样可用。这能够被用于当发布到maven库时,或者当另一个项目依赖于该库时。
Gradle有一个默认依赖的概念。这是当我们这样写时:
dependencies {
compile project(':libraries:lib2')
}
创建一个依赖于另一个发布类库的类库时,你需要指定使用哪一个:
dependencies {
flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}
重点1:请注意到已发布的配置是全版本的,包括所有的构建类型,并且需要被引用。
重点2:当开启非默认发布时,maven发布插件将会发布这些额外的版本作为额外的包(带有classifier)。这意味着发布到maven库并不能真正地兼容。你应该发布一个单版本,或者开启所有的配置用于发布内部项目依赖。