应用项目中整合了测试应用的构建。因此不需要再有一个独立的测试项目。
4.1单元测试在1.1中所提到的单元测试支持,请点击这里。本章节的剩下部分介绍了“工具测试(instrumentation tests)”,能够运行在真机或者模拟机上,其要求是要构建一个测试APK文件。
4.2基础配置如之前所提到的,main
资源集后面就是androidTest
资源集,默认情况下位于src/androidTest
,使用这个资源集使得测试APK被构建并能够安装到设备中,从而可以使用Android测试框架来测试应用,包括Android单元测试,instrumentation tests以及uiautomator tests。清单中的<instrumentation>
节点是被生成的,但是你可以创建一个src/androidTest/AndroidManifest.xml
文件来添加测试清单(manifest)的其他组件。
还有一些值能够在instrumentation测试应用中配置。(详情请查阅DSL参考)
- testApplicationId
- testInstrumentationRunner
- testHandleProfiling
- testFunctionalTest
如前所述,上述信息被配置在defaultConfig
对象:
android {
defaultConfig {
testApplicationId "com.test.foo" testInstrumentationRunner "android.test.InstrumentationTestRunner"
testHandleProfiling true
testFunctionalTest true
}
}
测试应用清单文件的instrumentation节点中targetPackage
属性的值自动化填充为测试应用的包名。即使在defaultConfig
中或者在构建类型对象中自定义,该值并不会发生改变。这也就是清单文件被自动生成的部分原因。
另外,androidTest
资源集能够配置自己的依赖关系。默认情况下,应用和其自身的依赖被添加到测试app的类路径中,但是可以使用下面的片段进行扩展:
dependencies {
androidTestCompile 'com.google.guava:guava:11.0.2'
}
测试app是由assembleAndroidTest
任务构建的。这并不是对主assemble
任务的依赖,而是当测试准备运行时自动调用的。
当前只有一个构建类型能够被测试。默认情况下是debug
构建类型。但是可以进行如下配置:
android {
//...
testBuildType "staging"
}
4.3解决主apk和测试apk之间的冲突
当instrumentation测试运行时,主APK和测试APK共享相同的类路径。如果主APK和测试APK使用相同的库(如Guava)的不同版本时,Gradle构建会失败。如果gradle并不捕获这一点,你的应用会在测试版和正常版表现地不同(包括任何会崩溃的情况)。
为了让应用构建成功,请确保两个APK都使用相同版本的库。如果错误来自于间接依赖(在你自己的build.gradle中没有声明的库),仅仅在需要的地方(compile
或者androidTestCompile
)添加最新的依赖即可。你也可以使用Gradle解决冲突机制。你可以通过运行以下代码检查依赖树:./gradlew :app:dependencies
和 ./gradlew :app:androidDependencies
。
如前所述,check
任务需要一个连接的设备,并通过祖先任务connnectedCheck
启动。这个任务会依赖connectedDebugAndroidTest
。这个任务做了以下几件事:
- 确保app和测试app被构建(依赖于
assembleDebug
和assembleDebugAndroidTest
)。 - 安装两款应用
- 运行测试
- 卸载两款应用
如果有多个设备连接,所有的测试都会平行运行在所有连接的设备上。如果其中一个测试失败,在任何设备中,构建都将会失败。
4.5测试Android库测试Android库项目和测试Android应用项目是完全相同的。唯一的区别在于,整个库以及依赖作为测试app的一个库被自动添加。结果是,测试APK不仅仅包括了自身的代码,也包含了库本身及其依赖。androidTest
任务变成仅仅安装(卸载)测试APK(因为没有其他APK可供安装)。
当运行单元测试时,Gradle输出一个HTML格式的报告可供轻松地查看结果。Android插件构建并拓展HTML报告用于从所有连接设备中汇集。所有的测试结果以xml文件的形式存储在build/reports/androidTests/
中(和常规的jNnit存储在build/reports/tests
相似)。该路径可配置如下:
android {
//...
testOptions {
resultsDir = "${project.buildDir}/foo/results"
}
}
android.testOptions.resultsDir
的值请参考:Project.file(String)。
4.6.1多项目报告
在带有多应用和多库的多项目的搭建中,当同时运行所有测试时,可能生成一个所有测试的测试报告是很有用的。
为了做到这一点,可用一个不同的gradle插件:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.6'
}
}
apply plugin: 'android-reporting'
这应该被用在根项目中,也就是setting.gradle旁边的build.gradle。
之后,从根文件夹开,以下的命令将会运行所有的测试并汇集成报告:
gradle deviceCheck mergeAndroidReports --continue
4.7Lint支持注意:
--continue
选项确保了所有子目录在内的所有测试都能够被运行,即使其中有失败的情况。
你可以在一个指定版本运行lint检查(如下),例如:./gradlew lintRelease
或者全版本的lint检查(./gradlew lint
),在这种情况下会产生一个描述指定版本的报告。你可以通过添加lintOption部分来配置lint(如下)。你应该添加一些典型的部分:详见。
android {
lintOptions {
// turn off checking the given issue id's
disable 'TypographyFractions','TypographyQuotes'
// turn on the given issue id's
enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// check *only* the given issue id's
check 'NewApi', 'InlinedApi'
}
}
5.构建版本
新构建系统的一个目标就是能够对同一个应用创建不同的版本。
主要使用情况有两个:
- 一个应用的不同版本。例如,一个免费/demo版本和一个专业版本
- 同一个应用在Google Play商店打包分发多个详见:http://developer.android.com/google/play/publishing/multiple-apks.html
- 前两者的组合
目标是能够在同一个项目中生成不同的应用,而不是使用同一个库的不同应用项目。
5.1产品渠道产品渠道(product flavor)定义了一个项目应用构建的自定义版本。单个的项目可以有不同的渠道,从而生成的应用不同。
这个新的概念被设计用于帮助版本差异非常小的情况下。如果当问到“这是否是同一个应用?”时,如果是,那么通过库项目去实现可能更适合一些。
产品渠道通过一个叫做productFlavors
的特定领域容器声明:
android {
//....
productFlavors {
flavor1 {
//...
}
flavor2 {
...
}
}
}
上例中将会创建两个渠道,分别是flavor1
和flavor2
5.2构建类型+产品渠道=构建版本注意:渠道的名称不能喝已经存在的构建类型名称冲突,也不能使用
androidTest
和test
资源集的名称。
正如我们前面所看到的,每一个构建类型生成一个新的APK文件。产品渠道也是相同的:项目的输出变成所有可能构建类型和产品渠道的组合。每一个(构建类型,产品渠道)组合被称为构建版本。例如,在默认的debug
和release
构建类型中,上面的例子可以生成四个构建版本:
- Flavor1 - debug
- Flavor1 - release
- Flavor2 - debug
- Flavor2 - release
没有渠道的项目依然拥有构建版本,使用的是单个默认的default
渠道/配置.
每一个渠道的完整配置如下:
android {
//...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
flavor1 {
applicationId "com.example.flavor1"
versionCode 20
}
flavor2 {
applicationId "com.example.flavor2"
minSdkVersion 14
}
}
}
注意到android.productFlavors.*
对象是ProductFlavor
,其类型和android.defaultConfig
对象类型相同。这意味着它们共享相同的属性。
defaultConfig
为所有的渠道提供了基础的配置,每一个种渠道能够覆盖它的任何值。在上述的例子中,配置信息以如下结尾:
- flavor1
- applicationId: com.example.flavor1
- minSdkVersion: 8
- versionCode: 20
- flavor2
- applicationId: com.example.flavor2
- minSdkVersion: 14
- versionCode: 10
通常情况下,构建类型配置会覆盖其他配置。例如,构建类型的applicationIdSuffix
追加在产品渠道的applicationId
后面。有一些在构建类型和产品渠道都能够设置的情况。在这种情况下,例如signingConfig
就是这种属性。这使得所有发布包共享这些签名配置。通过设置android.buildTypes.release.signingConfig
或者对每一个包通过设置自己的android.productFlavors.*.signingConfig
来分别使用签名配置。
和构建类型相似,产品渠道也是通过自己的资源集来贡献代码。上述的例子创建了四个资源集:
android.sourceSets.flavor1
位置为src/flavor1android.sourceSets.flavor2
位置为src/flavor2/android.sourceSets.androidTestFlavor1
位置为 src/androidTestFlavor1/android.sourceSets.androidTestFlavor2
位置为 src/androidTestFlavor2/
这些资源集通过构建类型和android.sourceSets.main
被用于构建APK。下面的规则用于处理所有被用于构建单个APK的情况:
- 所有的代码(
src/*/java
)共同用于多文件夹来生成单个输出。 - 清单被共同合并到单个的清单中。这允许产品渠道拥有不同的组件、权限,和构建类型相似。
- 所有的资源遵循覆盖的优先级。构建类型覆盖产品渠道,产品渠道覆盖
main
资源集。 - 每个构建版本生成自己的R类。各个版本直接不存在共享。
最后,和构建类型一样,产品渠道可以拥有自己的依赖。例如,如果渠道用于生成基于广告的app或者付费的app,每一个渠道拥有自己的广告sdk依赖。
dependencies {
flavor1Compile "..."
}
在这个例子中,文件src/flavor1/AndroidManifest.xml
可能需要包含网络权限
每个版本同样也创建了额外的资源集:
android.sourceSets.flavor1Debug
位于src/flavor1Debug
android.sourceSets.flavor1Release
位于src/flavor1Release
android.sourceSets.flavor2Debug
位于src/flavor2Debug
android.sourceSets.flavor2Release
位于src/flavor2Release
他们比构建类型拥有更高的优先级,并且允许自定义版本等级。
5.5构建和任务我们之前看到的,每一种构建类型创建自己的assmble名字
任务,但是构建版本是构建类型和产品渠道的组合。
当使用产品类型时,更多的任务会被创建,如:
assemble构建版本名
assemble构建类型名
assemble产品渠道名
第1项允许你直接构建单个的版本,例如aseembleFlavor1Debug
。
第2项允许你构建所有已给构建类型的apk文件。例如assembleDebug
将会构建flavor1Debug
和flavor2Debug
。
第3项允许你构建给定渠道的所有APK文件。例如,assembleFlavor1
将会构建assembleFlavor1Debug
和assembleFlavor1Release
。
任务assemble
会构建有可能的版本。
在一些情况下,可能会想要基于多种条件下创建相同应用的多个版本。
考虑一个游戏作为例子,该游戏有一个demo版本和一个付费版本并且想要使用多apk支持中的ABI过滤条件。3个ABI和2个版本需要生成6个APK文件(不考虑构建类型)。
但是,支付版本的代码对于相对应的3个ABI版本是相同的,因此创建6个渠道并不是一个好方法。
取而代之的是,配置两个渠道版本,所有的构建版本应该自动构建可能的组合。
这个功能是通过渠道规格(flavor dimension)来实现的。渠道被设置到指定的规格:
android {
//...
flavorDimensions "abi", "version"
productFlavors {
freeapp {
dimension "version"
//...
}
paidapp {
dimension "version"
...
}
arm {
dimension "abi"
...
}
mips {
dimension "abi"
...
}
x86 {
dimension "abi"
...
}
}
}
android.flavorDimensions
数组定义了可能的规格。每一个产品渠道指定一个规格。
从指定了规格的产品渠道(free app,paid app)以及构建类型(debug,release),能够创建如下的构建版本:
- x86-freeapp-debug
- x86-freeapp-release
- arm-freeapp-debug
- arm-freeapp-release
- mips-freeapp-debug
- mips-freeapp-release
- x86-paidapp-debug
- x86-paidapp-release
- arm-paidapp-debug
- arm-paidapp-release
- mips-paidapp-debug
- mips-paidapp-release
规格的顺序是通过 android.flavorDimensions
定义的,这一点非常重要。
每一个版本通过不同的产品渠道进行配置:
android.defaultConfig
- abi规格
- 版本规格
规格的顺序驱动了是哪个渠道去覆盖哪个渠道,这对于渠道的哪个资源值去替代另一个渠道的资源值而言非常重要。
渠道规格通过高优先级进行定义。在这种情况下:
abi
>version
>defaultConfig
多渠道项目也拥有额外的资源集,和构建版本资源集相似但是不包括构建类型:
android.sourceSets.x86Freeapp
位于src/x86Freeapp
android.sourceSets.armPaidapp
位于src/armPaidapp
这样允许渠道组合级别的自定义。他们比基本的渠道资源集拥有更高的优先级,但是优先级低于构建类型资源集。
5.7测试多渠道项目测试和简单的项目测试非常相似。
androidTest
资源集用于所有渠道的通用测试,而每个渠道可以有自己的测试。
如上所提及的,每个渠道的资源集按照如下形式被创建:
android.sourceSets.androidTestFlavor1
位于src/androidTestFlavor1
android.sourceSets.androidTestFlavor2
位于src/androidTestFlavor2/
相似地,它们可以有各自的依赖:
dependencies {
androidTestFlavor1Compile "..."
}
运行测试可以通过祖先任务deviceCheck
完成,或者通过主androidTest
任务。
每一个渠道都有自己的测试任务:androidTest版本名
,例如:
androidTestFlavor1Debug
androidTestFlavor2Debug
相似地,测试APK的构建和卸载安装按照每个版本进行:
- assembleFlavor1Test
- installFlavor1Debug
- installFlavor1Test
- uninstallFlavor1Debug
最后,HTML报告生成支持通过渠道的汇总。
测试结果和报告的位置如下所示,首先是渠道版,其次是汇总版:
- build/androidTest-results/flavors/渠道名
- build/androidTest-results/all/
- build/reports/androidTests/flavors/渠道名
- build/reports/androidTests/all/
或者可以自定义路径,但这仅仅改变了文件夹的根目录,但是仍然会对每个渠道创建子目录并汇集报告结果。
5.8构建配置Android Studio生成一个叫作BuildConfig
的类,包含了用于构建一个特定版本的常量值。你可以指定这些常量值来改变不同版本,例如:
private void javaCode() {
if (BuildConfig.FLAVOR.equals("paidapp")) {
doIt();
else {
showOnlyInPaidAppDialog();
}
}
以下是构建配置中含有的值:
boolean DEBUG
– if the build is debuggable.int VERSION_CODE
String VERSION_NAME
String APPLICATION_ID
String BUILD_TYPE
- 构建类型的名字,例如”release”String FLAVOR
– 渠道名称,例如”paid app”
如果项目使用渠道规格,将会生成额外的规格。使用上面的例子,会有如下的构建配置示例:
String FLAVOR = "armFreeapp"
String FLAVOR_abi = "arm"
String FLAVOR_version = "free app"
当你添加规格和渠道时,你应该停止使用没有意义的版本。例如你可能为了更快的测试,会定义一个使用你的Web API的渠道,以及一个使用硬编码的假数据。
第二个渠道仅仅在开发的时候有用,但是在发布版本的构建时却没有用处。你可以删除这个版本,改成使用variantFilter
,例如:
android {
productFlavors {
realData
fakeData
}
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("fakeData") && variant.buildType.name == "release") {
variant.ignore = true
}
}
}
在上面的配置中,你的项目只有三个版本:
realDataDebug
realDataRelease
fakeDataDebug
混淆插件在Android插件中被自动使用,如果构建类型通过minifyEnabled
属性开启了混淆,那么任务会被自动创建。
android {
buildTypes {
release {
minifyEnabled true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
flavor1 {
}
flavor2 { proguardFile 'some-other-rules.txt'
}
}
}
版本使用所有在这个构建类型以及产品渠道中声明的规则文件。
有两个默认规则文件:
- proguard-android.txt
- proguard-android-potimize.txt
这些文件位于SDK中。使用getDefaultProguardFile()
会返回文件的全路径。这些文件是相同的,除非开启了优化。
你可以在构建时自动移除所有未使用的资源。关于更多信息,请查阅资源缩减文档。
6.3操作任务基本的Java项目拥有有限的任务集共同创建输出。
classes
是编译java源代码的任务之一,缩写为:project.tasks.classes
。
在Android项目中有一些复杂。这是因为可能有大量相同的任务,并且它们的名字基于产品渠道和构建类型命名。
为了去解决这一点,android
对象有以下两个属性:
applicationVariants
(只用于app插件)libraryVariants
(只用于库插件)testVariants
(同时适用于app和库插件)
三者分别返回ApplicationVariant
,LibraryVariant
,TestVariant
的领域对象集合。
注意到,访问以上三种集合的任意一种都会引发所有任务的创建。这意味着在访问集合后不会发生任何重新配置的情况。
领域对象集合直接访问所有对象,或者通过更方便的过滤器。
android.applicationVariants.all { variant ->
....
}
所有版本类都含有以下属性:
- name 类型为String,表示版本名称,要求不重复。
- description 类型为String,人类可读的版本描述。
- dirName 类型为String,版本的子文件夹名称,要求不重复。可能会有多个文件夹,例如”debug/flavor”。
- baseName 类型为String,输出版本的基本名称,要求不重复。
- outputFile 类型为File,版本的输出。拥有读/写属性。
- processManifest 类型为ProcessManifest,用于处理清单的任务。
- aidlCompile 类型为AidlCompile,编译AIDL文件的任务。
- renderscriptCompile 类型为RenderscriptCompile,编译Renderscript的任务。
- mergeResources 类型为MergeResources,合并资源文件的任务。
- mergeAssets 类型为MergeAssets,合并assets的任务。
- processResources 类型为ProcessAndroidResources,用于处理和编译资源的任务。
- generateBuildConfig 类型为GenerateBuildConfig,生成BuildConfig类的任务
- javaCompile 类型为JavaCompile,编译Java代码的任务。
- processJavaResources 类型为Copy,处理Java资源的任务。
- assemble 类型为DefaultTask,对版本输出的祖先任务。
ApplicationVariant类添加了如下内容: - buildType 类型为BuildType,版本的构建类型。
- productFlavors 类型为List\<ProductFlavor>,版本的产品渠道可以不设置但是永远不为空。
- mergedFlavor 类型为ProductFlavor,android.defaultConfig和variant.productFlavors的合并。
- signingConfig 类型为SigningConfig,该版本的签名配置。
- isSigningReady 类型为boolean。为真时该版本拥有签名的所有必需信息。
- testVariant 类型为BuildVariant。TestVariant会测试该版本。
- dex 类型为Dex,编译代码的任务。如果是个库项目可以为空。
- packageApplication 类型为PackageApplication,构建最终APK的任务。如果是个库项目可以为空。
- zipAlign 类型为ZipAlign。打包apk的任务,如果是个库项目或者APK不能被签名时可以为空。
- install 类型为DefaultTask,安装任务,可以为空。
- unstall 类型为DefaultTask,卸载任务。
LibraryVariant类添加了一下内容:
- buildType 类型为BuildType,版本的构建类型。
- mergedFlavor 类型为ProductFlavor,默认配置值。
- testVariant 类型为BuildVariant,要测试的构建版本。
- packageLibrary 类型为Zip,打包库AAR压缩包的任务,如果不是库项目则为空。
TestVariant类添加了如下内容:
- buildType 类型为BuildType,版本的构建类型。
- productFlavors 类型为List\<ProductFlavor>,版本的产品渠道可以不设置但是永远不为空。
- mergedFlavor 类型为ProductFlavor,android.defaultConfig和variant.productFlavors的合并。
- signingConfig 类型为SigningConfig,该版本的签名配置。
- isSigningReady 类型为boolean。为真时该版本拥有签名的所有必需信息。
- testVariant 类型为BuildVariant。TestVariant会测试该版本。
- dex 类型为Dex,编译代码的任务。如果是个库项目可以为空。
- packageApplication 类型为PackageApplication,构建最终APK的任务。如果是个库项目可以为空。
- zipAlign 类型为ZipAlign。打包apk的任务,如果是个库项目或者APK不能被签名时可以为空。
- install 类型为DefaultTask,安装任务,可以为空。
- unstall 类型为DefaultTask,卸载任务。
- connectedAndroidTest 类型为DefaultTask,在连接设备上运行Android测试的任务。
- providerAndroidTest 类型为DefaultTask,使用扩展API运行Android测试的任务。
Android特定任务类型API:
- ProcessManifest
- 文件 manifestOutputFile
- AidlCompile
- 文件 sourceOutputDir
- RenderscriptCompile
- 文件 sourceOutputDir
- 文件 resOutputDir
- MergeResources
- 文件 outputDir
- MergeAssets
- 文件 outputDir
- ProcessAndroidResources
- 文件 manifestFile
- 文件 resDir
- 文件 assetsDir
- 文件 sourceOutputDir
- 文件 textSymbolOutputDir
- 文件 packageOutputFile
- 文件 proguardOutputFile
- GenerateBuildConfig
- 文件 sourceOutputDir
- Dex
- 文件 outputFolder
- PackageApplication
- 文件 resourceFile
- 文件 dexFile
- File javaResourceDir
- 文件 jniDir
- 文件 outputFile
- 在版本对象直接使用”outputFile”改变最终输出文件
- ZipAlign
- 文件 inputFile
- 文件 outputFile
- 在版本对象直接使用”outputFile”改变最终输出文件
每一个任务类型的APK是有限的,这是由于Gradle的工作方式以及Android插件是如何建立的。
首先,Gradle的任务仅仅配置了输入输出的位置以及可能的选项。因此,任务仅仅定义了输入输出。
其次,大多数任务的输入并不是无关紧要的,通常来自于资源集和构建类型以及产品渠道的混合。为了保持构建文件易于阅读和理解,开发者通过领域特定语言对这些对象进行微调就能够进行构建,而不是深入改变项目的输入和选项。
另外也需要注意到的是,除了ZipAlign任务类型,所有的任务需要设置私有的数据才能工作。这意味着不可能手动创建这些类型的新任务。
主观上API是能够修改的。通常情况下当前的API是围绕着给定的输入输出(当任务能够添加额外必须的处理时)。反馈是很重要的,特别是一些需求不可见时。
关于Gradle的其他任务(DefaultTask, JavaCompile, Copy, Zip),请参考Gradle文档。
6.3设置Java语言等级你可以使用compileOptions
代码块选择编译器的语言等级,默认情况下是基于compileSdkVersion
的值。
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章