更好的阅读体验请访问:http://download.csdn.net/detail/jw20082009jw/9516782
一个全新的构建系统
原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。
简介
DSL引用:如果你在寻找build.gradle文件里面所列选项的清单,请移步DSL reference
新构建系统的几个目标:
l 让代码和资源的重用更加简单
l 让创建应用级别变量更简单,不管是发布多apk版本还是统一应用不同支持的版本
l 让配置、扩展、定制构建过程更加简单
l 优秀的IDE集成
为什么选择Gradle?
Gradle不仅仅是一个优秀的构建系统,而且是一个允许你通过插件定制构建逻辑的优秀构建工具。以下是一些吸引我们选择Gradle的特性:
l 使用基于Groovy的DSL,用来描述和处理构建逻辑
l 构建文件基于Groovy并可以与声明性元素混合使用,通过使用DSL以及使用声明性代码来控制DSL元素来提供定制逻辑
l 内置了通过Maven 和Ivy的依赖管理
l 非常弹性,允许但又不强制使用推荐的方式来做事
l 插件都可以暴露他们自己的DSL语句和调用接口给构建文件来使用
l Ide友好
环境要求:
l Gradle 2.2
l SDK以及Build Tools 19.0.0。某些特性可能会需要更加新近的版本
基本项目设置
描述项目构建的内容在根目录下一个叫build.gradle里面。(build构建系统的使用请移步Gradle User Guide)
简单的构建文件
最简单的android项目有以下build.gradle
该构建文件主要包含三大部分:
Buildscript{...} 配置此处代码来驱动构建。在这部分,声明了他使用jCenter仓库,jCenter仓库有一个classpath依赖于Maven组件。这个组件是一个包含了1.3.1版Android plugin for Gradle的库。注意:这一特性只会影响运行构建的代码,而不是项目本身。项目本身需要自行声明所引用的仓库和依赖。这一特性将会在后面被覆盖掉。
然后是对于com.android.application插件的应用,该插件用来构建Android 应用。
最后,android{...}配置了android构建过程中的所有参数。这就是Android DSL的入口关键点。默认情况下,只需要提供目标编译版本和build-tools的版本。通过compileSdkVersion和buildtoolsVersion两个属性来完成。
特别注意:你只需要使用com.android.application插件,同时应用java插件将会导致构建错误
注意:你也需要一个local.properties文件来设置SDK的路径,同时一个已存在的SDK也是必要的,使用sdk.dir属性来设置sdk路径。或者你也可以设置一个叫做ANDROID_HOME的环境变量来代替以上做法。两种方法没有差别,你可以使用任意一种你喜欢的方式。示例local.properties文件如下:
项目结构
上面提到过的简单的构建文件需要一个默认的目录结构。Gradle遵循约定优于配置的概念,尽可能的提供了各种合理的默认选项值。上面项目包括了两大主要组件“source sets”,一个是用来做主源码另一个是测试源码。分别以下面形式存在。
l src/main/
l src/androidTest/
在这个目录下的每个源码组件内部都有子目录。不管是Java或者Android插件,java源码和java资源的位置都如下:
l java/
l resources/
对于android插件,会有更多的文件和目录被划分出来:
l AndroidManifest.xml
l res/
l assets/
l aidl/
l rs/
l jni/
l jniLibs/
这就是说源码文件*.java文件都应当被放在src/main/java目录下,并且主清单文件就是 src/main/AndroidManifest.xml文件。
注意:src/androidTest/AndroidManifest.xml当他是自动生成的时候并没有什么用处。
配置目录结构
当默认的项目结构并不够用的时候,你可能需要来自己配置。查看纯java项目下这一步怎么完成请移步this page in gradle documentation。
Android插件使用了类似的语法,但是由于使用了其本身的源码设置,这些都在android块内部完成了。下面是一个示例,使用了老的项目结构(在eclipse下使用的)来定位源码文件并且重新对应了androidTest到tests目录下。
注意:由于老的项目结构里吧所有的源文件(java、aidl和RenderScript)都放在同一个目录下,我们需要重新对应sourceSet下的这些新组件到相同的源目录下。
注意:setRoot()移动了整个sourceSet(包括他的子目录)到另一个新的目录下,这里把 src/androidTest/*移动到了tests/*下,这个方法是android特有的,并不会在java的sourceSets上生效。
构建任务
一般的任务
将一个插件应用到构建文件上会自动生成一系列的构建任务以供运行。Java插件和android插件都做了这件事情。任务列表如下:
l assemble 装配生成本项目输出文件的任务
l Check 运行所有检查的任务
l Build 执行assemble和check两个任务
l Clean 该任务清理掉项目所有的输出文件
Assemble、check和build任务实际上什么也不干,他们只是一个任务标签来让插件来添加独立任务来干实际的事情。
这样就可以让你不管是什么类型项目或者应用了不同类型的插件,都可以总是调用相同的任务。例如,使用了findbugs插件将会创建一个新的任务,并且check将会依赖改新任务,当你调用check任务时该任务就会被调用。
运行下面的命令你可以获得高级任务:
l gradle tasks
查看完整任务列表并且查看任务见的依赖可以使用:
l gradle tasks --all
注意:Gradle自动监听任务已声明的输入和输出
运行第二次build任务对你的项目不产生任何影响,gradle将会通知你所有任务都是UP-TO-DATE。意味着没有任何工作需要做。这就允许任务间更好的彼此依赖而不需要任何不必要的构建操作。
Java项目任务
这里有两个java插件产生的最重要的任务,主要标签任务的依赖。
l Assemble
n Jar
该任务生成输出
l Check
n Test
该任务运行测试
Jar任务本身也会直接或间接的依赖于其他任务:例如classes任务可以编译java代码。测试代码是用testClasses编译,但是很少调用他,因为test任务依赖了他(classes也是一样)
一般来说,你可能只会调用assemble和check,然后忽略其他任务。你可以查看java插件的所有任务以及他们的描述在这里。
Android任务
Android插件使用了同样的任务结构来保持和其他插件的兼容,并且添加了一个多的标签任务。
l Assemble
该任务装配项目的输出
l Check
该任务运行所有的检查
l connectedCheck
该任务运行需要一个已连接设备或者模拟器的检查,他们会并行运行在所有已连接设备上
l deviceCheck
使用APIs连接到远程设备来执行检查。用于CI服务器。
l Build
该任务执行assemble和check
l Clean
该任务清理项目下的所有输出
新增的标签任务是必需的因为可以在不需要已连接设备的时候运行常规检查。注意build任务并不依赖deviceCheck或者connectedCheck。
一个安卓项目至少包含两个输出:一个调试版apk和一个发布版的apk。每个都有他自己的标签任务来完成其单独构建。
l Assemble
n assembleDebug
n assembleRelease
他们都依赖了其他任务来执行构建一个apk的多个步骤。Assemble任务依赖了该两个任务,所以调用assemble将会构建两个apk。
Tips: Gradle支持在命令行上任务名的驼峰法缩写。例如:
gradle aR
和
gradle assembleRelease是一样的,因为没有其他的任务匹配”aR”
标签任务check有他自己的依赖:
l Check
n Lint
l connectedCheck
n connectedAndroidTest
l deviceCheck
n 所依赖的任务会在其他插件实现test扩展的时候生成。
最后,是所有构建类型(debug,release,test)都有的一些插件任务用来安装和卸载的,因为他们都可以被安装(需要签名),例如:
l installDebug
l installRelease
l uninstallAll
n uninstalDebug
n uninstallRelease
n uninstallDebugAndroidTest
简单的自定义构建
Android插件提供了一个优秀的专用语言来从构建系统直接定制大部分的事情。
Manifest条目
通过专用语言可以配置最重要的manifest条目,就像:
l minSdkVersion
l targetSdkVersion
l versionCode
l versionName
l applicationId(有效的包名——更多信息移步ApplicationId versus PackageName)
l testApplicationId(测试APK会用到)
l testInstrumentationRunner
例如:
构建属性的完整清单可以移步Android Plugin DSL Reference.
把这些manifest属性放到构建文件里面最大的吸引力来自于可以动态的选择这些值。例如,你可以从文件中读取或者使用自定义逻辑来获取版本名。
注意:方法名不要与该作用范围内已经存在的getters方法冲突。例如defaultConfig{...}调用getVersionName()就会自动使用getter方法defaultConfig.getVersionName()而不是自定义的方法。
构建方式
默认情况下,安卓插件自动设置项目来构建调试版本和发布版本的应用。两者最大的差别在于设备上的调试能力,和apk的详细签名信息。调试版本使用一个自动生成并众所周知的用户名/密码来签名(为了避免构建过程中的弹出请求)。正式版在构建过程中不会被签名,需要在后面来做。
这个配置项通过BuildType来实现。默认情况下,两个实例会被生成,一个debug,一个release。安卓插件允许定制这两个实例以及创建其他类似的构建方式。这些都在buildType专用语言容器内完成。
上述片段实现了如下:
l 配置默认的debug构建类型
n 设置其包名为<app applicationId>.debug,以使在同一台机器上能够安装debug和release两个apk。
l 创建一个新的BuildType命名为jnidebug并且配置他为debug构建类型的一个副本。
l 继续配置jnidebug,开启jni组件的调试模式,并且添加一个另外的包名。
创建一个新的Build Type就想在buildTypes内使用一个新元素一样容易。要么调用initWith()要么就使用一个闭包来配置他。在DSL Reference里面可以看到自定义构建类型中可以配置的所有属性。
如果想要修改构建属性,Build Types可以被用来指定特定的代码和资源。每种构建类型,都有一个新的sourceSet匹配,默认位于src/<buildtypename>/,例如src/debug/java目录中添加的资源只会被编译进调试apk里面。这就意味着Build Type名字不能够使用main或者androidTest(这是插件强制要求的),并且必须唯一。
就像其他的sourceSet一样,自定义的BuildType的源目录页可以被重定位:
此外,对于每个Build Type,一个新的assemble<BuildTypeName>任务会被创建。例如:assembleDebug。前面已经提到过assembleDebug和assembleRelease任务,这就是那么的来源。当debug和release作为Build Types被预构建的时候,他们的任务也会自动被创建。根据这条规则,上面的build.gradle片段也会生成一个assembleJnidebug任务,并且assemble会依赖他就想依赖assembleDebug和assembleRelease任务一样的方式。
Tips:记住你可以输入gradle aJ来运行这个assembleJnidebug任务。
可能会有的情况:
l 只在调试模式下用到的权限,并不出现到发布版的应用中
l 定制调试功能的实现
l 调试模式下使用不同的资源文件(例如一个资源文件值和签名关联)
那么BuildType里的代码/资源会以下列方式被使用:
l buildType内manifest代码会被合并入相应应用的manifest里(可以直接设置相应的manifest里的权限来实现上述需求)
l 调试模式的代码可以使用另外一个源码路径
l buildType里面的资源是叠加在主资源之上,会替换已经存在的值。
签名配置
签名一个应用需要如下(APK签名具体详情请移步Signing Your Application):
l 一个keystore(签名文件)
l 一个keystore密码
l 一个签名用户名
l 一个签名密码
l 签名文件类型
存储位置,以及用户名,包括密码和签名类型都来自Signing Configuration。默认,有一个设置好的调试用配置文件使用的调试的签名文件,使用了众所周知的默认用户名和密码。
调试用的签名文件存放在$HOME/.android/debug.keystore(ios系统),如果没有会自动生成。调试模式下BuildType会自动被设置为使用debug签名配置。
创建一个新的配置或者修改默认内置的配置都是可以的。这些都是通过signingConfigs来实现:
上面的片段,改变了调试用签名文件的路径为项目根目录下。这回自动影响到任何使用了改项的Build Types,在本例中使调试Build Type。并且创建了一个新的签名配置和一个使用新配置的Build Type。
注意:只有在默认路径下的调试签名文件会被自动生成。改变签名文件路径之后是不会自动生成的。使用默认路径下调试用的签名文件来生成一个其他名字的SigningConfig是会自动生成。总之,是否自动生成是和签名文件的路径相关,而不是配置的名字。
注意:签名文件的路径通常都与项目的根目录相关,但是也可以是绝对路径,尽管并不推荐(调试模式下不一样,因为他是自动生成的)。
注意:如果你把这些文件加入进了版本控制(例如git),你可能不希望密码被明文放置在文件里。下面的 following Stack Overflow post提供了一种从控制台读取值,或者从环境变量读取值的方式。
依赖管理,安卓库和多项目设置
Gradle项目会对其他组件有依赖关系。这些组件可以是其他的外部二进制包,或者其他gradle项目。
依赖一个二进制包
本地包
设置依赖于一个外部jar,你需要使用compile配置项添加一条依赖。下面的代码片段添加了对于libs目录下的所有jar的依赖。
注意:dependencies元素是标准的Gradle接口,不位于android元素内。
Compile配置项是用来编译主应用的。所有compile的都会被添加到编译路径中,并且会被打包进apk。下面是其他添加依赖的可能配置项:
l Compile:主应用
l androidTestCompile:测试应用
l debugCompile:调试Build Type
l releaseCompile:发布版Build Type
因为不可能构建一个apk却没有一个关联的Build Type,所以一个apk总是以两个(或者更多)配置项来配置:compile和<buildtype>Compile。创建一个新的Build Type会自动创建一个基于其名字来生成的新配置。如果调试版需要使用定制化的库当发布版并不需要的时候的话这一特性将会显得非常实用(例如崩溃上报),或者当他们依赖于同一个库的不同版本的时候(如何处理版本冲突,详情请见Gradle documentation)。
远程组件
Gradle支持从Maven和Ivy仓库拉取组件。首先仓库必须被添加进列表,并且依赖必须以对应Maven或者Ivy声明组件的方式声明该依赖。
注意:jcenter()是指定仓库地址的一种快捷方式。Gradle支持包括远程仓库和本地仓库。
注意:gradle相当直接的跟从所有依赖,这就意味着如果存在依赖自身,也会被拉取下来。
设置依赖的更多信息,可以阅读Gradle的用户指引here,和DSL文档here.
多个项目设置
Gradle项目同样可以依赖于其他gradle项目通过使用多项目设置。一个多项目设置通常通过使用所有的项目做为一个给定根项目的子目录来实现。例如,给定如下的结构:
MyProject/
+ app/
+ libraries/
+ lib1/
+ lib2/
当我们指定3个项目。Gradle将会使用如下的名字来引用他们:
:app
:libraries:lib1
:libraries:lib2
每个项目都会有他自己的build.gradle来声明其自身是如何构建的。此外,在根目录下会有一个叫做settings.gradled的文件来声明这些项目。目录结构如下:
MyProject/
| settings.gradle
+ app/
| build.gradle
+ libraries/
+ lib1/
| build.gradle
+ lib2/
| build.gradle
Settings.gradle的内容相当简单,他定义了哪个目录是实际上的Gradle项目:
:app项目有可能会依赖libraries,声明依赖的方式如下:
关于多项目设置更多信息请访问here.
库项目
在上面的多项目设置,:libraries:lib1和:libraries:lib2可以是一个java项目,:app项目将会使用他们输出的jar。然而,如果你想要共享访问了android接口的代码或者是使用android风格的资源,这些库就不能够只是常规的java项目,他们必须是android的库项目。
创建一个库项目
一个库项目和一个常规的安卓项目非常相似,但有几个不同点。因为构建一个库和构建一个应用是不同的,所以会使用一个不同的插件。实际上两个插件共享了大部分代码并且都是有同一个jar,由com.android.tools.build.gradle包提供。
创建了一个使用Api 23来编译的库项目。源目录、构建方式和依赖都是以和应用相同的方式来设置和定制化的。
一个普通应用项目和一个库项目的不同点
一个库项目的主要输出是一个.aar包(就是android的压缩文件)。他包括了编译后端的代码(作为jar文件或者native的.so文件)和资源文件(manifest,res,assets)。一个库项目也会生成一个测试apk来独立于应用测试该库项目。和应用项目使用同样的标签任务。拥有多个build types和product flavors(见下文),有生成一个或多个版本的能力。注意大部分Build Type的配合不适用于库项目。然而你还是可以根据是否被一个项目使用或者仅仅只是用作测试来使用不同的sourceSet以此更改库的内容。
引用一个库
引用一个库和引用其他的应用项目是同样的方式:
注意:如果你有多个库,顺序会很重要。就像在老的构建系统中project.properties文件中的依赖顺序很重要是一样的道理。
库的发布
默认情况下一个库值发布其release部分。这部分会被所有引用该库的项目所使用,无论这些项目是使用的其中哪个部分来构建其自身。这是现存的一个限制,我们正在想办法解决这些限制。你可以控制具体发布哪个部分:
注意发布配置文件引用完整的变量名。当没有flavors的时候Release和debug是唯一可用的。如果你想要使用flavors来更改默认的发布变量,你可以如下写:
意识到发布多变量意味着多个aar文件是很重要的,而不是单个aar包含了多个变量。每个aar包中只有一个变量。发布一个变量意味着将该aar作为gradle项目的一个可用输出。之后可能会发布到一个maven仓库中,或者被另外一个项目所引用。
Gradle的理念是使用默认的组件,当你如下写的时候就是使用的默认的:
对另一个已发布的组件创建依赖,你需要指定使用哪一个:
很重要:注意发布的配置文件时一个完整变量,包括构建类型,并且需要按此方法被引用。
很重要:当发布一个非默认的,maven的发布插件会发布这些额外的变量通过额外的包(有标识)。这就意味着发布到maven库不那么兼容。你应该要么发布一个单变量到仓库,要么使用完整配置发布一个项目内的依赖。
测试
构建一个测试应用已经被集成进入了应用项目中。再也不需要单独的测试项目。
单元测试
单元测试在1.1中被添加支持,please see this separate page.余下部分会描述“工具测试”可以被运行于真机(或者模拟器)并且需要构建一个独立的测试apk.
基本配置
前面提到过,main源目录设置的下一个就是androidTest的源目录设置,默认位于src/androidTest.使用该源目录设置来构建一个测试apk,将其部署到一个机器上并根据安卓的测试框架来测试应用的使用。包括安卓单元测试,工具测试和自动化测试。
测试app的<instrumentation>是默认激活的,你也可以创建一个src/androidTest/AndroidManifest.xml文件来向测试manifest中添加其他组件。
配置<instrumentation>属性,测试应用中这里有几个值可以被设置(详情请见DSL reference)
l testApplicationId
l testInstrumentationRunner
l testHandleProfiling
l testFunctionalTest
像前面的一样,这些也在defaultConfig对象中配置:
测试应用manifest中Instrumentation节点的targetPackage属性自动使用被测试应用的包名填充,就算他通过defaultConfig或者build type对象来实现定制化也不例外。这就是这部分manifest被自动创建的原因之一。
另外,androidTest可以被配置为有他自己的依赖。默认,应用和其依赖将会被添加进测试应用的构建路径中,但是这个可以按照如下片段被扩展:
测试应用通过assembleAndroidTest任务来构建。他并不是assemble任务的一个依赖,而是会在测试应用被设置并且跑起来的时候自动调用。
目前只有一个build type被测试,默认就是调试build type。但是这个是可以被配置的:
解决测试apk和主apk之间的冲突
当测试用例运行的时候,主apk和测试apk共享了同一个classpath。如果主apk和测试apk使用了同一个库的不同版本那么Gradle的构建会失败。如果gradle不处理这种情况,你的应用就有可能会在测试包和应用包之间产生差异(包括在其中一种包中发生崩溃)
要想构建成功,就需要确保apk都使用同一个版本。如果错误是由于一个非直接引用的依赖引起(一个你没在你的build.gradle中提及的库),只需要添加一个较新版本的依赖到配置文件中就可以了(“compile”或者”androidTestCompile”)。你也可以使用Gradle’s resolution strategy mechanism。你可以通过使用./gradlew :app:dependencies 和./gradlew :app:androidDependencies来检查依赖树。
运行测试
前面提及过的,检查需要一个运行了标签任务connectedCheck的已连接设备。这一步依赖了任务connectedDebugAndroidTest,所以会运行改任务。该任务会执行如下:
l 确保app和测试app已经构建(依赖assembleDebug和assembleDebugAndroidTest)
l 安装两个app
l 运行测试
l 卸载两个app
如果已连接多个设备,测试会平行运行于所有已连接设备。如果其中某个测试或者某个设备失败了,构建就会失败。
测试安卓库
测试一个安卓库项目的方式和一个应用项目的方式其实是一致的。唯一的区别是整个库(包括库的依赖)都会被自动添加测试应用的库依赖中。结果就是测试apk不止包含了其自身的代码,也包含了库和库的依赖。库的manifest会被合并入测试app的manifest中(就像其他任何项目引用该库一样)。androidTest任务除了安装测试apk其他都是一样的。
测试报告
当运行单元测试,Gradle输出结果为易读的html报告。Android插件基于此并且扩展了html报告来整合所有已连接设备的结果。所有的测试结果都保存为xml文件置于build/reports/androidTests/下(就像常规jUnit结果被保存在build/reports/tests)。这项可以通过以下方式配置:
android.testOptions.resultsDir的值应该是Project.file(String)类型的
多项目报告
在一个有多个应用和库的项目设置里,当同时运行所有测试的时候,为所有测试生成一个报告就变的很有意义。
在同一个组件内提供了另一个插件来解决这个问题,如下应用:
这个应该被放在根项目,和settings.gradle同目录的build.gradle文件中。
在根目录下,如下命令会运行所有测试并整合报告:
Gradle deviceCheck mergeAndroidReports --continue
注意:--continue选项确保了所有的测试,包括来自所有子项目中的测试就算其中某个失败了也会被运行。如果没有这个在第一个测试失败发生的时候可能就会打断运行就会导致不是所有测试都被运行过。
Lint 支持
你可以为指定的变量运行lint,例如./gradlew lintRelease,或者对所有变量(./gradlew lint),并提供针对该变量一个制定问题的测试报告。你可以按照如下方式添加一个lintOptions来配置lint。通常情况下只会用到其中很少的一部分,详情见the DSL reference for all available options.
构建变量
新的构建系统的目标之一就是使可以针对同一个应用能够创建不同的版本。
这里有两种主要的方案:
1、同一个应用的不同版本,例如,一个免费/示例版本 vs 一个 “pro”/付费版应用。
2、同一个应用针对google应用商店multi-apk打不同的应用包。
i. 详情请见
http://developer.android.com/google/play/publishing/multipl-apks.html
3、1和2的结合
目标就是能够从同一个项目中生成这些不同的apk,而不是使用一个库和2+个项目。
Product flavors
一个product flavor定义了使用该项目的定制应用版本。一个项目可以有不同的flavors来改变项目生成。
这个理念是设计来提供帮助当版本差别很小的时候。如果“是否同一应用”的答案是对,然后可能就是重温库项目。
Product flavors使用productFlavors来声明:
这里创建了两个flavors,分别是flavor1和flavor2。
注意:flavors的名字不能和已存的build type名字冲突,或者和androidTest和test的source sets冲突。
Build Type + Product Flavor = Build Variant
就像我们前面看到过的一样,每一个build type生成一个新的apk。Product Flavors也做了同样的事情:项目的输出有可能就是build types和product flavors的结合。每个(Build Type,Product Flavor)结合就是Build Variant.例如,对于默认的debug和release build types,上面的例子会生成四个build variants。
l Flavor1-debug
l Flavor1-release
l Flavor2-debug
l Flavor2-release
项目没有flavors还是有Build Variants,但是只有一个default flavor/config被使用,没有名字,使得variants的列表和build types的列表很像。
Product Flavor配置
每个flavors都是使用一个闭包来配置:
注意android.productFlavors.*对象是ProductFlavor的类型和android.defaultConfig对象的类型相同。这就意味着他们共享同样的属性。
defaultConfig为所有flavors提供了基础配置并且所有flavor都可以重写任何值。在上面的例子中,最后的配置为:
l Flavor1
n applicationId: com.example.flavor1
n minSdkVersion: 8
n versionCode: 20
l Flavor2
n applicationId: com.example.flavor2
n minSdkVersion: 14
n versionCode: 10
通常,Build Type的配置会覆盖其他的配置。例如,Build Type的applicationIdSuffix会添加到Product Flavor的applicationId后。有一些情况下设置项可以在Build Type和Product Flavor中都可以被设置。在这个例子中,不同地方有不同的情况。例如,signingConfig是一个这种属性。通过设置android.buildTypes.release.signingConfig,可以让所有的发布版包都共用了同一个SigningConfig,或者改变每一个发布版包的SigningConfig,通过单独设置每一个android.productFlavors.*.signingConfig对象。
SourceSets 和Dependencies
就像Build Types,Product Flavors也通过他自己的sourceSets来改变代码和资源。上面的例子创造了四个sourceSets:
l android.sourceSets.flavor1
路径 src/flavor1/
l android.sourceSets.flavor2
路径 src/flavor2/
l android.sourceSets.androidTestFlavor1
路径 src/androidTestFlavor1/
l android.sourceSets.androidTestFlavor2
路径 src/androidTestFlavor2/
和android.sourceSets.main和Build Type的sourceSet一起的这些sourceSet是用于构建apk的。下面的规则是用来处理所有用来构建单个apk的sourceSets.
l 所有源码(src/*/java)会被当做多个包一起使用来生成单个输出。
l Manifest会被合并入单个manifest。这就允许Product Flavors有不同的组件或者权限,就像Build Types一样。
l 所有的资源(Android res和assets)都使用优先覆盖的方式,Build Type会覆盖Product Flavor,覆盖主sourceSet。
l 每一个Build Variant会依据资源生成其自己的R类(或者其他自动生成的代码)。Variants之间没有任何共享。
最后,就想Build Type是,Product Flavors也可以有他自己的依赖。例如,如果flavors是用来生成一个ads-based应用和一个paid应用,其中一个就可能会依赖Ads的工具包,然而另一个就不会。
在某些特定情况下,src/flavor1/AndroidManifest.xml文件会需要包含网络访问权限。
会为每个variants创建额外的sourcesets:
l android.sourceSets.flavor1Debug
路径 src/flavor1Debug/
l android.sourceSets.flavor1Release
路径 src/flavor1Release/
l android.sourceSets.flavor2Debug
l android.sourceSets.flavor2Release
这些比build type的sourceSets有更高的优先权,并且允许从variant级别进行定制。
构建过程和任务
一开始我们看到每个build type都创建了他自己的assemble<name>任务,但是这种build variants是build type和product flavor二者的结合。
当product flavors被使用了,更多的类assemble任务就被创建。就是:
1. Assemble<Variant Name>
2. Assemble<Build Type Name>
3. Assemble<Product Flavor Name>
#1 允许直接构建一个variant.例如assembleFlavorDebug.
#2 允许使用特定的build type构建所有apk.例如 assembleDebug会构建flavor1Debug和Flavor2Debug variants.
#3 允许使用特定的flavor构建所有apk.例如assembleFlavor1 会构建Flavor1Debug和Flavor1Release variants.
Assemble任务会构建所有可能的variants.
多flavor的variants
有时候,你会想要从同一项目中基于不同的条件创建不同的版本。
例如,google应用商店支持4中不同过滤方式的multi-apk。创建不同的apk基于每种过滤方式下都要求Product Flavors有使用多种尺寸的能力。
有一个例子,一个游戏有一个演示版和一个收费版并且想要使用multi-apk支持中的ABI过滤。有3中ABIs和两个版本的应用程序。6个apk需要被生成(并不是计算不同build types的variants)。
然而,支付版本的三个ABIs的代码都是一样的,所以简单的创建6个flavors并不是正确方式。
取而代之的是,有两种flavors尺寸,并且variants需要自动构建所有可能的组合。
该特性通过使用flavor dimensions的方式被实现。Flavors被赋予了特定的尺寸:
android.flavorDimension列表中定义了所有可能的尺寸,以及其次序。每一个Product Flavor都被分配到一个尺寸。
根据如下的已经被分配尺寸的Product Flavor[freeapp,paidapp]和[x86,arm,mips]以及Build Types[debug,release],如下的build variants将会被创建:
l X86-freeapp-debug
l X86-freeapp-release
l Arm-freeapp-debug
l Arm-freeapp-release
l Mips-freeapp-debug
l Mips-freeapp-release
l X86-paidapp-debug
l X86-paidapp-release
l Arm-paidapp-debug
l Arm-paidapp-release
l Mips-paidapp-debug
l Mips-paidapp-release
Android.flavorDimensions中定义尺寸的顺序非常重要。
每一个variant都被多个Product Flavor对象所配置:
l android.defaultConfig
l 一个来自于abi尺寸
l 一个来自版本尺寸
尺寸的顺序决定了是哪个flavor覆盖另外的,这很重要当一个资源的值在一个flavor中用来替换了一个低优先级的flavor中的值的时候。
Flavor尺寸的优先顺序如下:
Abi > Version > defaultConfig
多个Flavors的项目也会有额外的sourceSets,类似variant的sourceSets但是少了build type:
l android.sourceSets.x86Freeapp
路径 src/x86Freeapp/
l android.sourceSets.armPaidapp
路径 src/armPaidapp/
l Etc...
这就允许在于flavor组合的级别来进行定制。这些比基础flavor sourcesets有着更高的优先级,但是比build type sourcesets的优先级更低。
测试
测试多flavors项目和一个简单的项目非常相似。
AndroidTest的sourceset贯穿于整个flavors中被用于通用测试。每个flavor也可以有他自己的测试。
就像前面提到过的,测试每个flavor的sourceSets被生成在下:
l android.sourceSets.androidTestFlavor1
路径 src/androidTestFlavor1/
l android.sourceSets.androidTestFlavor2
路径 src/androidTestFlavor2/
类似的,这些也可以有他们自己的依赖:
通过运行主标签任务deviceCheck可以运行测试,或者当使用到了flavors的时候主任务androidTest任务页会实现类似标签任务的功能
每个flavor都有他自己的任务来执行测试:androidTest<VariantName>.例如:
l androidTestFlavor1Debug
l androidTestFlavor2Debug
类似的,测试apk的构建任务和安装/卸载任务都是对于每个variant的:
l assembleFlavor1Test
l InstallFlavor1Debug
l InstallFlavor1Test
l uninstallFlavor1Debug
l ...
最后html生成报告支持基于flavor的整合。
测试结果和报告的路径如下,首先是对每个flavor版本,然后是对于整合版本的:
l Build/androidTest-results/flavors/<FlavorName>
l Build/androidTest-results/all/
l Build/reports/androidTests/flavors<FlavorName>
l Build/reports/androidTests/all/
修改路径,只会改变根目录,同样也会生成flavor的子目录并整合结果/报告。
BuildConfig
编译时,android studio生成了一个叫做BuildConfig的类,该类包含了在构建一个variant需要被用到的常量。你可以检查这些常量的值,从而在不同的variants中改变行为,例如:
下面是BuildConfig中包含的一些值:
l Boolean DEBUG - 如果构建是可调试的。
l Int VERSION_CODE
l String VERSION_NAME
l String APPLICATION_ID
l String BUILD_TYPE - build type的名字,例如:“release”
l String FLAVOR - flavor的名字,例如:“paidapp”
如果项目使用了flavor尺寸,会生成额外的值。使用上面的例子,这里是一个BuildConfig的例子:
l String FLAVOR = “armFreeapp”
l String FLAVOR_abi = “arm”
l String FLAVOR_version = “freeapp”
Variants过滤
当你添加尺寸和flavors的时候,你可以使用结束不产生任何意义的variants。例如,你可以定义一个flavor使用的WEB API和一个flavor使用硬编码伪数据,来加快测试。第二个flavor只是在开发中有帮助,在构建发布版中不会有意义。你可以使用variantFilter闭包来移除这些,就像:
通过上面的配置,你的项目会只包含三个variants:
l realDataDebug
l realDataRelease
l fakeDataDebug
Variant中所有你可以检查的属性见DSL reference。
高级构建定制
运行ProGuard
ProGuard插件会自动被Android插件所应用,该任务会自动创建如果Build Type中已经通过minifyEnabled属性来设置运行ProGuard.
Variants使用声明在其build type和product flavors中的所有规则。
有两个默认的规则文件:
l Proguard-android.txt
l Proguard-android-optimize.txt
他们都位于sdk中,使用getDefaultProguardFile()就会返回该文件的完整路径。除了optimizations其他都是一样的。
资源压缩
你也可以在构建的时候自动移除掉无用资源。更多信息,请查看Resource Shrinking文档
操作任务
基本的java项目有数量有限的任务来共同工作以生成结果。
Classes任务是用来编译java源码的任务
通过简单的在build.gradle脚本中使用classes就可以很容易的访问该任务。只是project.tasks.classes的快捷方式。
在安卓项目中,这可能有点复杂因为有很多同样的任务他们的名字是基于build types和product flavors生成的。
为了改善这个,android有两个属性:
l applicationVariants(只在应用插件中有)
l libraryVariants(只在库应用的插件中有)
l testVariants(两个插件都可用)
所有三个都返回一个DomainObjectCollection 分别对应ApplicationVariant,LibraryVariant和TestVariant对象。
注意访问任何的这些collections都会触发全部分五的创建。这就意味着在访问这些collections之后不应该发生任何事情。
DomainObjectCollection提供了对于所有对象的直接访问,或者通过filters可以很快捷的访问。
所有的这三个variant类共享了如下的属性:
applicationVariant类添加了如下属性:
LibraryVariant类添加了如下属性:
TestVariant类添加了如下:
Android特定任务类型的调用接口:
每个任务的接口方式都是限制的,这是由于Gradle的工作方式以及安卓插件的设置方式。
首先,Gradle就意味着有的任务只会被配置来确定输入/输出位置和可能的选项标识。所以这里,任务(某些)只定义了输入/输出。
其次,这些任务大部分的输入都是值得重视的,经常来自于sourceSets,Build Types 和Product Flavor的混合值。为了保持构建文件的简单易读易理解,目标就是让开发者能修改build通过简单的从DSL中拉出这些对象,而不是深入输入和任务选项并去改变他们。
同时注意,除了对于ZipAlign任务类型,所有其他的任务类型都需要设置单独的数据来使得他们工作。这就意味着不可能来手动创建这些类型的任务。
这个API是会变的。总的来说现在的API是围绕访问任务输入输出。
设置语言级别
你可以使用compileOptions块来选择编译器使用的语言界别。默认是基于compileSdkVersion值来选择的。