为了账号安全,请及时绑定邮箱和手机立即绑定

Android Gradle 使用教程(下)

标签:
Android

接上篇:Android Gradle 使用教程(上)

4.测试

应用项目中整合了测试应用的构建。因此不需要再有一个独立的测试项目。

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

4.4运行测试

如前所述,check任务需要一个连接的设备,并通过祖先任务connnectedCheck启动。这个任务会依赖connectedDebugAndroidTest。这个任务做了以下几件事:

  • 确保app和测试app被构建(依赖于assembleDebugassembleDebugAndroidTest)。
  • 安装两款应用
  • 运行测试
  • 卸载两款应用

如果有多个设备连接,所有的测试都会平行运行在所有连接的设备上。如果其中一个测试失败,在任何设备中,构建都将会失败。

4.5测试Android库

测试Android库项目和测试Android应用项目是完全相同的。唯一的区别在于,整个库以及依赖作为测试app的一个库被自动添加。结果是,测试APK不仅仅包括了自身的代码,也包含了库本身及其依赖。androidTest任务变成仅仅安装(卸载)测试APK(因为没有其他APK可供安装)。

4.6测试报告

当运行单元测试时,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

注意:--continue选项确保了所有子目录在内的所有测试都能够被运行,即使其中有失败的情况。

4.7Lint支持

你可以在一个指定版本运行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.构建版本

新构建系统的一个目标就是能够对同一个应用创建不同的版本。
主要使用情况有两个:

  1. 一个应用的不同版本。例如,一个免费/demo版本和一个专业版本
  2. 同一个应用在Google Play商店打包分发多个详见:http://developer.android.com/google/play/publishing/multiple-apks.html
  3. 前两者的组合

目标是能够在同一个项目中生成不同的应用,而不是使用同一个库的不同应用项目。

5.1产品渠道

产品渠道(product flavor)定义了一个项目应用构建的自定义版本。单个的项目可以有不同的渠道,从而生成的应用不同。

这个新的概念被设计用于帮助版本差异非常小的情况下。如果当问到“这是否是同一个应用?”时,如果是,那么通过库项目去实现可能更适合一些。
产品渠道通过一个叫做productFlavors的特定领域容器声明:

android {
    //....

    productFlavors {
        flavor1 {
            //...         
        }

        flavor2 {
            ...
        }
    }
}

上例中将会创建两个渠道,分别是flavor1flavor2

注意:渠道的名称不能喝已经存在的构建类型名称冲突,也不能使用androidTesttest资源集的名称。

5.2构建类型+产品渠道=构建版本

正如我们前面所看到的,每一个构建类型生成一个新的APK文件。产品渠道也是相同的:项目的输出变成所有可能构建类型和产品渠道的组合。每一个(构建类型,产品渠道)组合被称为构建版本。例如,在默认的debugrelease构建类型中,上面的例子可以生成四个构建版本:

  • Flavor1 - debug
  • Flavor1 - release
  • Flavor2 - debug
  • Flavor2 - release

没有渠道的项目依然拥有构建版本,使用的是单个默认的default渠道/配置.

5.3产品渠道配置

每一个渠道的完整配置如下:

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来分别使用签名配置。

5.4资源集和依赖

和构建类型相似,产品渠道也是通过自己的资源集来贡献代码。上述的例子创建了四个资源集:

  • android.sourceSets.flavor1 位置为src/flavor1
  • android.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名字任务,但是构建版本是构建类型和产品渠道的组合。
当使用产品类型时,更多的任务会被创建,如:

  1. assemble构建版本名
  2. assemble构建类型名
  3. assemble产品渠道名

第1项允许你直接构建单个的版本,例如aseembleFlavor1Debug

第2项允许你构建所有已给构建类型的apk文件。例如assembleDebug将会构建flavor1Debugflavor2Debug

第3项允许你构建给定渠道的所有APK文件。例如,assembleFlavor1将会构建assembleFlavor1DebugassembleFlavor1Release

任务assemble会构建有可能的版本。

5.6多渠道版本

在一些情况下,可能会想要基于多种条件下创建相同应用的多个版本。
考虑一个游戏作为例子,该游戏有一个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"
5.9过滤版本

当你添加规格和渠道时,你应该停止使用没有意义的版本。例如你可能为了更快的测试,会定义一个使用你的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
6.高级构建自定义
6.1运行混淆

混淆插件在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.2缩减资源

你可以在构建时自动移除所有未使用的资源。关于更多信息,请查阅资源缩减文档。

6.3操作任务

基本的Java项目拥有有限的任务集共同创建输出。

classes是编译java源代码的任务之一,缩写为:project.tasks.classes
在Android项目中有一些复杂。这是因为可能有大量相同的任务,并且它们的名字基于产品渠道和构建类型命名。

为了去解决这一点,android对象有以下两个属性:

  • applicationVariants(只用于app插件)
  • libraryVariants(只用于库插件)
  • testVariants(同时适用于app和库插件)

三者分别返回ApplicationVariantLibraryVariantTestVariant领域对象集合

注意到,访问以上三种集合的任意一种都会引发所有任务的创建。这意味着在访问集合后不会发生任何重新配置的情况。

领域对象集合直接访问所有对象,或者通过更方便的过滤器。

android.applicationVariants.all { variant ->
   ....
}

所有版本类都含有以下属性:

  1. name 类型为String,表示版本名称,要求不重复。
  2. description 类型为String,人类可读的版本描述。
  3. dirName 类型为String,版本的子文件夹名称,要求不重复。可能会有多个文件夹,例如”debug/flavor”。
  4. baseName 类型为String,输出版本的基本名称,要求不重复。
  5. outputFile 类型为File,版本的输出。拥有读/写属性。
  6. processManifest 类型为ProcessManifest,用于处理清单的任务。
  7. aidlCompile 类型为AidlCompile,编译AIDL文件的任务。
  8. renderscriptCompile 类型为RenderscriptCompile,编译Renderscript的任务。
  9. mergeResources 类型为MergeResources,合并资源文件的任务。
  10. mergeAssets 类型为MergeAssets,合并assets的任务。
  11. processResources 类型为ProcessAndroidResources,用于处理和编译资源的任务。
  12. generateBuildConfig 类型为GenerateBuildConfig,生成BuildConfig类的任务
  13. javaCompile 类型为JavaCompile,编译Java代码的任务。
  14. processJavaResources 类型为Copy,处理Java资源的任务。
  15. assemble 类型为DefaultTask,对版本输出的祖先任务。
    ApplicationVariant类添加了如下内容:
  16. buildType 类型为BuildType,版本的构建类型。
  17. productFlavors 类型为List\<ProductFlavor>,版本的产品渠道可以不设置但是永远不为空。
  18. mergedFlavor 类型为ProductFlavor,android.defaultConfig和variant.productFlavors的合并。
  19. signingConfig 类型为SigningConfig,该版本的签名配置。
  20. isSigningReady 类型为boolean。为真时该版本拥有签名的所有必需信息。
  21. testVariant 类型为BuildVariant。TestVariant会测试该版本。
  22. dex 类型为Dex,编译代码的任务。如果是个库项目可以为空。
  23. packageApplication 类型为PackageApplication,构建最终APK的任务。如果是个库项目可以为空。
  24. zipAlign 类型为ZipAlign。打包apk的任务,如果是个库项目或者APK不能被签名时可以为空。
  25. install 类型为DefaultTask,安装任务,可以为空。
  26. unstall 类型为DefaultTask,卸载任务。

LibraryVariant类添加了一下内容:

  1. buildType 类型为BuildType,版本的构建类型。
  2. mergedFlavor 类型为ProductFlavor,默认配置值。
  3. testVariant 类型为BuildVariant,要测试的构建版本。
  4. packageLibrary 类型为Zip,打包库AAR压缩包的任务,如果不是库项目则为空。

TestVariant类添加了如下内容:

  1. buildType 类型为BuildType,版本的构建类型。
  2. productFlavors 类型为List\<ProductFlavor>,版本的产品渠道可以不设置但是永远不为空。
  3. mergedFlavor 类型为ProductFlavor,android.defaultConfig和variant.productFlavors的合并。
  4. signingConfig 类型为SigningConfig,该版本的签名配置。
  5. isSigningReady 类型为boolean。为真时该版本拥有签名的所有必需信息。
  6. testVariant 类型为BuildVariant。TestVariant会测试该版本。
  7. dex 类型为Dex,编译代码的任务。如果是个库项目可以为空。
  8. packageApplication 类型为PackageApplication,构建最终APK的任务。如果是个库项目可以为空。
  9. zipAlign 类型为ZipAlign。打包apk的任务,如果是个库项目或者APK不能被签名时可以为空。
  10. install 类型为DefaultTask,安装任务,可以为空。
  11. unstall 类型为DefaultTask,卸载任务。
  12. connectedAndroidTest 类型为DefaultTask,在连接设备上运行Android测试的任务。
  13. 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
    }
}
点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
28
获赞与收藏
201

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消