如果是你想干的事情,在别人看起来可能是很难的一件事,不过你自己很喜欢,你不会觉得很苦。我开始创业那会是28岁。对我来讲,我创业的目的不是为了自己当老板,我希望有一个平台有一个环境,我可以控制一些资源,让我去创造一个新的产品和服务;
—— 周鸿祎
Gradle是一种依赖管理工具,基于Groovy语言,面向Java应用为主,它抛弃了基于XML的各种繁琐配置,取而代之的是一种基于Groovy的领域特定(DSL)语言。
当然,我们现在最多都是在Android Studio的项目中,和我一样没有接触过的就当看看我的学习笔记吧。
随便找到了一个Android Sdk中的一个Sample代码,看了一下它的gradle脚本。如下所示:
apply plugin: 'com.android.application'
android {
compileSdkVersion 21 #build的sdk版本
buildToolsVersion "21.1.2" #build的工具版本
defaultConfig { #默认配置,versionname,versioncode等
applicationId "com.example.hello"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes { #编译方式
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies { #第三方依赖库
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:21.0.3'
}
我们从阅读上看来是很简单的,我们完全没有学习过gradle也能够看到它的脚本大概的意思。那么,我们该如何书写此类脚本能?这类的脚本又能够帮助我们做一些什么复杂的事情?
由于在网上也没有找到一个比较全面的Android Gradle教程,所以,自己根据官方的入门文档:
http://tools.android.com/tech-docs/new-build-system/user-guide
翻译文如下:
简单的构建文件,最简单的 纯Java项目的build.gradle如下所示:
apply plugin: 'java'
这里配置使用了Gradle内置的 Java 插件。该插件提供用于构建并测试 Java 应用程序所需要的东西。
最简单的 Android 项目的 build.gradle 则是以下内容:
buildscript { repositories { mavenCentral() }
dependencies { classpath 'com.android.tools.build:gradle:0.11.1' }
}
apply plugin: 'android'
android { compileSdkVersion 19 buildToolsVersion "19.0.0" }
在这个 Android 构建文件中,有3个主要部分:
buildscript: 配置了驱动构建的代码。
apply plugin: android插件,配置构建Android需要的东西。
android:配置了用于 android 构建的所有参数。
Tips:你应该只配置使用这个android插件。如果同时配置使用了java插件也会导致构建错误。
基本项目开始于两个名为“source sets”的组件。即主源代码和测试代码。它们分别在:
里面的每个文件夹中都存在对应的源代码组件的文件夹。
对于 Java 和 Android 插件,Java 源代码和 Java 资源的位置如下:
对于Android 插件,Android所特有的额外的文件和文件夹是:
注: src/androidTest/AndroidManifest.xml是不需要的,因为它会被自动创建。
当IDE环境没有自动的生成构建的Gradle脚本,我们可以根据 Gradle 文档,为一个Java 项目重新配置 sourceSets可以通过如下方法实现:
sourceSets { main { java { srcDir 'src/java' }
resources { srcDir 'src/resources' }
}
}
注: srcDir实际上会将给定的文件夹添加到现有的源文件夹列表中 (这在Gradle 文档中没有提及,但这是实际的行为)。
如果要替换默认的源文件夹,您就要使用传入一个路径数组的srcDirs来代替。以下是使用涉及的对象的另一种不同的方法:
sourceSets {
main.java.srcDirs = ['src/java']
main.resources.srcDirs = ['src/resources']
}
Android 插件使用类似的语法,但因为它使用它自己的sourceSets,所以在android对象里面来实现。
这里有一个例子,使用旧的项目结构的主源码并重新映射androidTest sourceSet 到tests文件夹:
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')
}
}
在构建文件中配置使用一个插件,将自动创建一系列要运行的构建任务。Java 插件和 Android 插件约定的任务如下:
assemble,check和build这些任务,实际上不做任何事情。他们是锚记任务,用于让插件添加实际的任务去做这些事情。这允许您能够调用同样的任务,无论项目的类型是什么,或者是配置使用了什么插件。
例如,配置使用findbugs插件将创建一个新的任务和使check任务依赖于它,使得每当调用check任务时都会调用到它。
您可以从命令行中运行下面的命令来获取更高级别的任务:
gradle tasks
下面的命令可以得到一个完整的任务列表,并且看到任务运行之间的依赖关系:
gradle tasks --all
Java 插件主要创建两个任务,它们是主要锚记任务的依赖任务:
jar任务本身会直接或间接地依赖其他任务: 例如,classes任务用于编译 Java 代码。
testClasses任务用于编译测试,但它很少会被调用,因为test任务依赖于它 (以及classes任务)。
一般情况下,你将可能永远只调用assemble或check,而无视其他任务。
Android 的插件使用相同的约定配置以兼容其他插件,并添加了另外的锚记任务:
新的锚记任务是有必要的,以便能够运行定期的检查而无需连接的设备。
注意到,build任务不依赖于deviceCheck或connectedCheck。
Android 项目具有至少两个输出: debug版本的APK 和release版本的 APK。这里的每一个输出都有自己的锚记任务,以便单独构建它们:
它们两个都依赖于执行构建一个 APK所需的多个步骤的其他任务。assemble任务取则依赖于这两个任务,所以调用 assemble 将会构建出这两个 APKs。
Tips:在命令行上,Gradle 支持任务名称的驼峰命名法的简写。例如: gradle aR 相当于输入 gradle
assembleRelease 只要没有其他任务匹配 “aR”
check锚记任务有它们自己的依赖项:
这个任务依赖于当其他插件实现了测试扩展点时创建的任务。
最后,该插件为所有构建类型 (debug、release、test)创建了omstal/uninstall 任务,只要他们可以被安装(需要签名)。
Android 插件提供了大量的 DSL,以通过构建系统直接地自定义大部分事情。通过 DSL 可以配置以下清单条目:
示例:
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
defaultConfig {
versionCode 12
versionName "2.0"
minSdkVersion 16
targetSdkVersion 16
}
}
在android元素的内部的defaultConfig元素是定义所有这些配置的地方。
以前版本的 Android 插件使用packageName来配置清单的“packageName”属性。 从 0.11.0开始,你应该在
build.gradle 中使用 applicationId 来配置清单中的“packageName”条目。 它消除了应用程序的包名(指它的
ID)和java 包名之间的所引起的混乱。
在构建文件中描述它的强大之处是它可以是动态的。
例如,可以从文件中的某处或使用一些自定义的逻辑读取版本信息:
def computeVersionName() {
...
}
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
defaultConfig {
versionCode 12
versionName computeVersionName()
minSdkVersion 16
targetSdkVersion 16
}
}
Tips: 不要使用在作用域内可能与已存在的getter函数有冲突的函数名称。例如 defaultConfig { …} 实例调用 getVersionName() 时将自动使用 defaultConfig.getVersionName() 的 getter 方法,而不是自定义的方法。
如果一个属性未通过 DSL 来设置,它将使用默认值。下表描述了对于未设置的属性的处理方式。
属性名称 | DSL 对象中的默认值 | 默认值 |
---|---|---|
versionCode | -1 | 如果在清单中存在,则使用清单中的值 |
versionName | null | 如果在清单中存在,则使用清单中的值 |
minSdkVersion | -1 | 如果在清单中存在,则使用清单中的值 |
targetSdkVersion | -1 | 如果在清单中存在,则使用清单中的值 |
applicationId | null | 如果在清单中存在,则使用清单中的值 |
testApplicationId | null | applicationId + “.test” |
testInstrumentationRunner | null | |
android.test.InstrumentationTestRunner | ||
signingConfig | null | null |
proguardFile | N/A (只设置) | N/A (只设置) |
proguardFiles | N/A (只设置) | N/A (只设置) |
第二列的值是很重要的,如果您在构建脚本中使用自定义逻辑查询这些属性的话。例如,您可以编写:
if (android.defaultConfig.testInstrumentationRunner == null) {
// assign a better default...
}
如果值仍然为null,那么在构建的时候它将会被设为第三列中的实际默认值,但是由于 DSL 元素不包含此默认值,因此您无法查询它。
这是为了防止解析应用程序的清单,除非真的很需要。
默认情况下,Android 插件自动将项目设置为生成应用程序的的debug和release版本。
这两个版本的不同,大多是围绕在调试一个安全的(非开发版的)设备的能力,以及 apk 怎么签名。
调试版本使用自动创建的密钥/证书签名,并且密钥/证书的用户名/密码是已知的(以防止构建过程中需要相关的信息)的。release版本在构建的时候不会进行签名,需要在之后进行签名。
这个配置是通过一个叫BuildType的对象来完成的。默认情况下,2 个实例会被创建,分别是debug版和release版。
Android 插件允许自定义这两个实例,以及创建其他的构建类型。它通过buildTypes DSL 容器来实现:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug.initWith(buildTypes.debug)
jnidebug {
packageNameSuffix ".jnidebug"
jniDebuggable true
}
}
}
上面的代码段可实现以下操作:
创建新的 Build Types 就是简单地在buildTypes下添加一个新的元素,然后调用 initWith()或者是使用一个闭包来配置。
以下是可能用到的属性和它们的默认值:
属性名称 | debug的默认值 | release/其他的默认值 |
---|---|---|
debuggable | true | false |
jniDebuggable | false | false |
renderscriptDebuggable | false | false |
renderscriptOptimLevel | 3 | 3 |
applicationIdSuffix | null | null |
versionNameSuffix | null | null |
signingConfig | android.signingConfigs.debug | null |
zipAlignEnabled | false | true |
minifyEnabled | false | false |
proguardFile | N/A (只设置) | N/A (只设置) |
proguardFiles | N/A (只设置) | N/A (只设置) |
除了这些属性,Build Types还会影响到构建的代码和资源。
对每个Build Type都会创建一个自动匹配的sourceSet,默认位置为
src/<buildtypename>/
这意味着Build Type的名字不能为main或者是androidTest (这是插件所强制的),并且它们之间必须是唯一的。
与任何其他source set一样,生成类型的source set的位置也是可以重新设置的:
android {
sourceSets.jnidebug.setRoot('foo/jnidebug')
}
此外,对于每个Build Type,会创建一个新的assemble任务。
已经提到过的assembleDebug和assembleRelease这两个任务,这里也会讲一下它们是怎么来的。当debug和releaseBuild Types被预创建的时候,他们的任务也会被自动创建。然后,
上面的build.gradle片段也会生成一个assembleJnidebug任务,并且assemble将会依赖于它,就像它依赖于assembleDebug和assembleRelease任务一样。
Tips: 请记住您可以输入gradle aJ来运行assembleJnidebug任务。
可能会用到的情况:
BuildType的代码和资源通过以下方式被使用:
对应用程序进行签名,要求如下:
存储类型
签名文件的位置,key的名称,以及这两个密码和存储类型,一起构成了一个签名配置 ( SigningConfig类型)
默认情况下,有一个debug的配置,配置使用了一个debug keystore。这个keystore使用了一个已知的key和一个已知的密码。
这个debug keystore 位于$HOME/.android/debug.keystore,并且会在不存在时被创建。debug Build Type被设置为自动使用此debug
你也可以创建其他配置,或者自定义某个默认的内置配置。通过signingConfigs DSL 容器来实现:
android {
signingConfigs {
debug {
storeFile file("debug.keystore")
}
myConfig {
storeFile file("other.keystore")
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
buildTypes {
foo {
debuggable true
jniDebuggable true
signingConfig signingConfigs.myConfig
}
}
}
上面的代码段把debug keystore的位置修改为在项目的根位置下。这会自动影响到任何设置为使用它的Build Types,在这里,影响到的是debug Build Type。
代码的代码还创建了一个新的Signing Config和使用新配置的新的Build Type 。
Tips:只有位于默认位置下的debug keystores才会被自动创建。如果debug
keystore的位置被更改了,它将不会在需要时自动创建。创建一个使用一个不同的名称SigningConfig,但使用了默认的debug
\
Tips: keystore的路径通常使用项目根目录的相对路径,但也可以是使用绝对路径,尽管这不推荐 (除了自动创建的debug keystore)。——–
ProGuard 是通过 Gradle plugin for ProGuard version 4.10来进行支持的。ProGuard 插件会被自动配置使用,并且如果Build Type通过minifyEnabled属性配置为运行ProGuard,对应的任务将被自动创建。
android { buildTypes { release { minifyEnabled true proguardFile getDefaultProguardFile('proguard-android.txt') }
}
productFlavors { flavor1 { }
flavor2 { proguardFile 'some-other-rules.txt' }
}
}
变种使用他们的构建类型中所声明的规则文件,product flavors(定制版本)则使用flavor中声明的规则文件。
这里有 2 个默认的规则文件
它们位于 SDK 中。使用getDefaultProguardFile()将返回的文件的完整路径。它们除了是否启用优化之外,其它都是相同的。
Gradle 项目可以对其他组件具有依赖关系。这些组件可以是外部的二进制包,或其他的 Gradle 项目。
本地包
要配置一个外部库 jar 包的依赖,您需要在compile配置中添加一个依赖关系。
dependencies {
compile files('libs/foo.jar')
}
android {
...
}
Tips:dependencies DSL 元素是标准的 Gradle API 的一部分,不属于android 元素内。
compile配置用于编译主应用程序。里面的所有内容都会被添加到编译类路径,并且打包到最终生成的 apk 当中。下面是添加依赖时其他可能用到的配置:
因为不可能构建一个没有任何关联的Build Type的 APK,apk 总是配置两个(或以上)的配置:compile和Compile。
创建一个新的Build Type会基于它的名字自动创建一个新的配置。
这可能会有用,比如debug版本需要使用一个自定义库(例如报告崩溃的信息),而release版本则不需要,或者是他们依赖于同一个库的不同版本的情况下。
Gradle 支持从 Maven 和 Ivy 仓库中拉取文件。
首先,这个仓库必须添加到列表当中,然后必须用Maven 或 Ivy 声明文件的方式声明这个
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.guava:guava:11.0.2'
}
android {
...
}
mavenCentral()是指定maven中央仓库的URL的快捷方法。Gradle支持远程和本地仓库。
Tips:Gradle 将遵循所有依赖关系的传递性。这意味着,如果一个依赖有它自己的依赖关系,这些依赖也会被拉取。
有关设置依赖关系的更多信息,请参阅 Gradle 用户指南(这里),和DSL文档(这里)。
Gradle 项目也可以通过使用多项目设置依赖于其他的 Gradle 项目。
一个多项目设置通常是通过让所有的项目作为给定根项目的子文件夹来实现。
例如,给定以下项目结构:
MyProject/
+ app/
+ libraries/
+ lib1/
+ lib2/
我们可以识别出3个项目。Gradle 将通过以下名称引用它们:
:app
:libraries:lib1
:libraries:lib2
每一个项目都有其自己的build.gradle文件,定义自己如何构建。
此外,在根路径下还将有一个叫settings.gradle的文件用于声明所有的项目。
这些文件的结构如下:
MyProject/
| settings.gradle
+ app/
| build.gradle
+ libraries/
+ lib1/
| build.gradle
+ lib2/
| build.gradle
settings.gradle的内容很简单:
include ':app', ':libraries:lib1', ':libraries:lib2'
它定义了哪个文件夹实际上是一个 Gradle 项目。
该:app项目可能依赖于libraries,这是通过声明如下的依赖关系来配置的:
dependencies {
compile project(':libraries:lib1')
}
在上面的多项目的设置中,:libraries:lib1和:libraries:lib2可以是Java项目,而:app Android项目将会使用到它们的jar包输出。
但是,如果你想共享访问了 Android API或使用了 Android-style的资源的代码,这些库项目就不能是普通的Java项目,而应该是 Android Library 项目。
创建库项目
Library项目与普通的 Android 项目非常相似,只有一些不同。
由于构建库项目与构建应用程序有些不同不同,所以使用的是不同的插件。这两个插件内部共享了大部分的相同的代码,并且它们都由同样的com.android.tools.build.gradle jar 包提供。
buildscript { repositories { mavenCentral() }
dependencies { classpath 'com.android.tools.build:gradle:0.5.6' }
}
apply plugin: 'android-library'
android { compileSdkVersion 15 }
上面的例子中创建了一个使用API 15编译的库项目。SourceSets和依赖关系与它们在应用程序项目中的处理方式一样,并且支持同样方式的自定义。
一个 Library 项目主要输出的是一个.aar包(它代表Android的归档文件)。它组合了编译代码(如一个jar文件或原生的.so文件)和资源(manifest,res,assets)。
一个库项目还可以生成测试apk,独立于应用程序测试这个库。
库项目用着同样的锚任务(assembleDebug, assembleRelease),所以构建这样一个项目的命令也没有任何区别。
对于其他的内容,库项目和应用程序项目的行为是一样的。。他们都有构建类型(build types)和产品定制(product flavors),并且都可以生成多个版本的aar。
需要注意的是,Build Type的大部分配置都不适用库项目。但是,您可以根据一个库项目是否被其他项目使用还是被测试,使用自定义 sourceSet 来更改库项目的内容。
引用一个库库和引用其他任何项目的方法是一样的:
dependencies {
compile project(':libraries:lib1')
compile project(':libraries:lib2')
}
Tips: 如果您有多个库,那么排序将非常重要。这类似于旧的生成系统中, project.properties 文件的依赖项的顺序的重要性。
默认情况下,一个库项目只发布它的release 变种程序。这变种程序将被所有引用该库的项目使用,无论那些项目构建的是哪种variant。这是由于 Gradle 限制而有的一个临时限制,我们正在努力消除这个问题。
您可以控制要发布哪一个如下所示:
android {
defaultPublishConfig "debug"
}
Tips,这个发布的配置名称引用的是完整的 变种程序
名称。release和debug,只在没有定义flavor时适用。如果你想在使用flavors时更改默认的发布
你可以这样写:
android {
defaultPublishConfig "flavor1Debug"
}
发布一个库项目的所有变种程序也是可以做到的。我们正计划在正常的项目对项目(project-to-project)的依赖(如上面的例子)时也可以这样做,但现在因为 Gradle 的限制(我们也在努力修复这些问题),还无法做到。
默认情况下没有启用发布所有变种程序。要启用它们
android {
publishNonDefault true
}
变种程序意味着发布多个aar文件,而不是发布一个包含多个变种程序的aar文件,能意识到这一点是非常重要的。每一个 aar 包都是包含一个单一的变种程序。
发布一个变种程序,意识着让这个可用的 aar 作为 Gradle 项目的输出文件。这个文件将会在发布到一个maven仓库中,或者另一个项目创建对这个项目依赖时用到。
Gradle 有一个默认文件的概念。它就是在编写下面的代码时用到的:
compile project(':libraries:lib2')
若要创建对一个项目的另一个已发布的文件的依赖,您需要指定使用哪一个:
dependencies {
flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}
/*
* @author zhoushengtao(周圣韬)
* @since 2015年8月29日 21:46:22
* @weixin stchou_zst
* @blog http://blog.csdn.net/yzzst
* @交流学习QQ群:341989536
* @私人QQ:445914891
/