Gradle Plugin for Android Development User Guide (2)

Gradle Plugin for Android Development User Guide (2)html

閱讀本文前請先閱讀第一部分:http://hujiaweibujidao.github.io/blog/2014/10/15/gradle-plugin-user-guide-1java

原文地址:http://tools.android.com/tech-docs/new-build-system/user-guidenode

Testing

[如今咱們能夠直接將test application集成到咱們的application project中,沒有必要再建立一個獨立的test project了]android

Building a test application is integrated into the application project. There is no need for a separate test project anymore.git

Basics and Configuration

[測試代碼默認存放在src/androidTest/ 目錄下,使用Android Testing Framework 咱們能夠建立unit tests,instrumentation tests, 和 uiautomator tests.]github

As mentioned previously, next to the main sourceSet is the androidTest sourceSet, located by default in src/androidTest/app

From this sourceSet is built a test apk that can be deployed to a device to test the application using the Android testing framework. This can contain unit tests, instrumentation tests, and later uiautomator tests.less

[test app的AndroidManifest.xml 文件是自動生成的,因此它不須要指定位置,此外,咱們不必設置test application的instrumentation節點的targetPackage 屬性,由於它會被test app的package name填充進去,這也就是爲何test app的Manifest文件是自動生成的]maven

The sourceSet should not contain an AndroidManifest.xml as it is automatically generated.ide

There are a few values that can be configured for the test app: [test app能夠指定的屬性]

testPackageName
testInstrumentationRunner
testHandleProfiling
testFunctionalTest

As seen previously, those are configured in the defaultConfig object:

android {
    defaultConfig {
        testPackageName "com.test.foo"
        testInstrumentationRunner "android.test.InstrumentationTestRunner"
        testHandleProfiling true
        testFunctionalTest true
    }
}

The value of the targetPackage attribute of the instrumentation node in the test application manifest is automatically filled with the package name of the tested app, even if it is customized through the defaultConfig and/or the Build Type objects. This is one of the reason the manifest is generated automatically.

Additionally, the sourceSet can be configured to have its own dependencies.
By default, the application and its own dependencies are added to the test app classpath, but this can be extended with

dependencies {
    androidTestCompile 'com.google.guava:guava:11.0.2'
}

[test app是經過任務assembleTest 來構建的,它不是main assemble任務的依賴項,因此它是在test運行時自動調用的。目前只有debug這個build type會被測試,固然也能夠自定義]

The test app is built by the task assembleTest. It is not a dependency of the main assemble task, and is instead called automatically when the tests are set to run.

Currently only one Build Type is tested. By default it is the debug Build Type, but this can be reconfigured with:

android {
    ...
    testBuildType "staging"
}

Running tests

[前面提到過,在全部已鏈接的設備上進行check的任務是connectedCheck,它依賴任務androidTest,該任務的工做是並行地在全部已鏈接的設備上運行測試,任何一個設備測試失敗的話,build就會失敗。測試的結果會保存在XML文件中,存放在build/androidTest-results 目錄下,固然也能夠修改目標目錄]

As mentioned previously, checks requiring a connected device are launched with the anchor task called connectedCheck.

This depends on the task androidTest and therefore will run it. This task does the following: [androidTest 任務的工做流程]

1 Ensure the app and the test app are built (depending on assembleDebug and assembleTest)
2 Install both apps
3 Run the tests
4 Uninstall both apps.

If more than one device is connected, all tests are run in parallel on all connected devices. If one of the test fails, on any device, the build will fail.

All test results are stored as XML files under

build/androidTest-results

(This is similar to regular jUnit results that are stored under build/test-results)

This can be configured with

android {
    ...

    testOptions {
        resultsDir = "$project.buildDir/foo/results"
    }
}

The value of android.testOptions.resultsDir is evaluated with Project.file(String)

Testing Android Libraries

[測試android library project和測試通常的application差很少,區別在於整個library和它的依賴項都會被自動添加到test app,Manifest文件也被整合到test app的Manifest中。此外,androidTest 任務只能安裝和卸載test APK]

Testing Android Library project is done exactly the same way as application projects.

The only difference is that the whole library (and its dependencies) is automatically added as a Library dependency to the test app. The result is that the test APK includes not only its own code, but also the library itself and all its dependencies.

The manifest of the Library is merged into the manifest of the test app (as is the case for any project referencing this Library).

The androidTest task is changed to only install (and uninstall) the test APK (since there are no other APK to install.)

Everything else is identical.

Test reports

[在進行單元測試時,Gradle會輸出一份HTML文檔形式的報告。Android插件在此之上進行擴展,輸出一份整合了全部已鏈接設備的測試結果的測試報告]

When running unit tests, Gradle outputs an HTML report to easily look at the results.

The Android plugins build on this and extends the HTML report to aggregate the results from all connected devices.

Single projects

The project is automatically generated upon running the tests. Its default location is build/reports/androidTests

This is similar to the jUnit report location, which is build/reports/tests, or other reports usually located in build/reports/<plugin>/

The location can be customized with

android {
    ...

    testOptions {
        reportDir = "$project.buildDir/foo/report"
    }
}

The report will aggregate tests that ran on different devices.

Multi-projects reports

[對於多項目的測試,可使用插件android-reporting 來將全部的測試結果輸出到一個單一的報告中,並且這個設置必須是要設置在根項目的build.gradle 文件中]

In a multi project setup with application(s) and library(ies) projects, when running all tests at the same time, it might be useful to generate a single reports for all tests.

To do this, a different plugin is available in the same artifact. It can be applied with:

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.6'
    }
}

apply plugin: 'android-reporting'

This should be applied to the root project, ie in build.gradle next to settings.gradle

[在項目根目錄下使用下面的命令能夠保證運行全部的測試並聚合全部的測試結果,其中的--continue 選項可以保證即便某個設備在測試過程當中出現了問題也不會打斷其餘的設備繼續測試]

Then from the root folder, the following command line will run all the tests and aggregate the reports:

gradle deviceCheck mergeAndroidReports --continue

Note: the --continue option ensure that all tests, from all sub-projects will be run even if one of them fails. Without it the first failing test will interrupt the run and not all projects may have their tests run.

Lint support

[lint能夠指出程序中可能出現的issue,android插件一樣支持]

As of version 0.7.0, you can run lint for a specific variant, or for all variants, in which case it produces a report which describes which specific variants a given issue applies to.

You can configure lint by adding a lintOptions section like following. You typically only specify a few of these; this section shows all the available options.

android {
    lintOptions {
        // set to true to turn off analysis progress reporting by lint
        quiet true
        // if true, stop the gradle build if errors are found
        abortOnError false
        // if true, only report errors
        ignoreWarnings true
        // if true, emit full/absolute paths to files with errors (true by default)
        //absolutePaths true
        // if true, check all issues, including those that are off by default
        checkAllWarnings true
        // if true, treat all warnings as errors
        warningsAsErrors true
        // 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'
        // if true, don't include source code lines in the error output
        noLines true
        // if true, show all locations for an error, do not truncate lists, etc.
        showAll true
        // Fallback lint configuration (default severities, etc.)
        lintConfig file("default-lint.xml")
        // if true, generate a text report of issues (false by default)
        textReport true
        // location to write the output; can be a file or 'stdout'
        textOutput 'stdout'
        // if true, generate an XML report for use by for example Jenkins
        xmlReport false
        // file to write report to (if not specified, defaults to lint-results.xml)
        xmlOutput file("lint-report.xml")
        // if true, generate an HTML report (with issue explanations, sourcecode, etc)
        htmlReport true
        // optional path to report (default will be lint-results.html in the builddir)
        htmlOutput file("lint-report.html")

   // set to true to have all release builds run lint on issues with severity=fatal
   // and abort the build (controlled by abortOnError above) if fatal issues are found
   checkReleaseBuilds true
        // Set the severity of the given issues to fatal (which means they will be
        // checked during release builds (even if the lint target is not included)
        fatal 'NewApi', 'InlineApi'
        // Set the severity of the given issues to error
        error 'Wakelock', 'TextViewEdits'
        // Set the severity of the given issues to warning
        warning 'ResourceAsColor'
        // Set the severity of the given issues to ignore (same as disabling the check)
        ignore 'TypographyQuotes'
    }
}

Build Variants

One goal of the new build system is to enable creating different versions of the same application.

There are two main use cases: [同一套程序代碼生成多種不一樣的結果的應用場景]

(1) Different versions of the same application

For instance, a free/demo version vs the 「pro」 paid application. [一個是展現應用,另外一個是真正的付費應用]

Same application packaged differently for multi-apk in Google Play Store.
See http://developer.android.com/google/play/publishing/multiple-apks.html for more information.

(2) A combination of 1. and 2.

The goal was to be able to generate these different APKs from the same project, as opposed to using a single Library Projects and 2+ Application Projects.

Product flavors

[product flavor是一個項目的特別定製版的應用程序輸出,單個項目能夠有不少不一樣的product flavors,它們的名稱不要和build type和sourceSet的名稱相同]

A product flavor defines a customized version of the application build by the project. A single project can have different flavors which change the generated application.

This new concept is designed to help when the differences are very minimum. If the answer to 「Is this the same application?」 is yes, then this is probably the way to go over Library Projects.

Product flavors are declared using a productFlavors DSL container:

android {
    ....

    productFlavors {
        flavor1 {
            ...
        }

        flavor2 {
            ...
        }
    }
}

This creates two flavors, called flavor1 and flavor2.

Note: The name of the flavors cannot collide with existing Build Type names, or with the androidTest sourceSet.

[重要的式子:每一個build type和product flavor的組合就是一個build variant]

Build Type + Product Flavor = Build Variant

As we have seen before, each Build Type generates a new APK.

Product Flavors do the same: the output of the project becomes all possible combinations of Build Types and, if applicable, Product Flavors.

Each (Build Type, Product Flavor) combination is called a Build Variant.

For instance, with the default debug and release Build Types, the above example generates four Build Variants:

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

[沒有配置flavor的項目會有一個默認的flavor配置]

Projects with no flavors still have Build Variants, but the single default flavor/config is used, nameless, making the list of variants similar to the list of Build Types.

Product Flavor Configuration

Each flavors is configured with a closure:

android {
    ...

    defaultConfig {
        minSdkVersion 8
        versionCode 10
    }

    productFlavors {
        flavor1 {
            packageName "com.example.flavor1"
            versionCode 20
        }

        flavor2 {
            packageName "com.example.flavor2"
            minSdkVersion 14
        }
    }
}

[ProductFlavor對象和android.defaultConfig 對象有相同的屬性,便可以使用相似的配置方式]

Note that the android.productFlavors.* objects are of type ProductFlavor which is the same type as the android.defaultConfig object. This means they share the same properties.

defaultConfig provides the base configuration for all flavors and each flavor can override any value. In the example above, the configurations end up being:

flavor1
packageName: com.example.flavor1
minSdkVersion: 8
versionCode: 20

flavor2
packageName: com.example.flavor2
minSdkVersion: 14
versionCode: 10

Usually, the Build Type configuration is an overlay over the other configuration. For instance, the Build Type's packageNameSuffix is appended to the Product Flavor's packageName.

[有些狀況下,咱們但願一個設置同時做用在build type和product flavor上,例如signingConfig 就是其中的一種配置,咱們既能夠設置全部的build type使用相同的SigningConfig,又能夠設置某些flavor使用某個特定的SigningConfig]

There are cases where a setting is settable on both the Build Type and the Product Flavor. In this case, it’s is on a case by case basis.

For instance, signingConfig is one of these properties.

This enables either having all release packages share the same SigningConfig, by setting android.buildTypes.release.signingConfig, or have each release package use their own SigningConfig by setting each android.productFlavors.*.signingConfig objects separately.

Sourcesets and Dependencies

[和build type相似,product flavor也會產生本身的sourceSets,這些sourceSets和build type的sourceSets以及android.sourceSets.main 組合起來構建最終的APK]

Similar to Build Types, Product Flavors also contribute code and resources through their own sourceSets.

The above example creates four sourceSets:

android.sourceSets.flavor1 Location src/flavor1/
android.sourceSets.flavor2 Location src/flavor2/
android.sourceSets.androidTestFlavor1 Location src/androidTestFlavor1/
android.sourceSets.androidTestFlavor2 Location src/androidTestFlavor2/

Those sourceSets are used to build the APK, alongside android.sourceSets.main and the Build Type sourceSet.

The following rules are used when dealing with all the sourcesets used to build a single APK: [重點:在構建APK過程當中處理全部源碼和資源的規則]

1 All source code (src/*/java) are used together as multiple folders generating a single output. [全部的源代碼都會整合到一塊兒做爲輸出]

2 Manifests are all merged together into a single manifest. This allows Product Flavors to have different components and/or permissions, similarly to Build Types. [全部的Manifest文件也都會整合成爲一個Manifest文件,其中product flavor和build type相似,均可以有不一樣的components或者permissions]

3 All resources (Android res and assets) are used using overlay priority where the Build Type overrides the Product Flavor, which overrides the main sourceSet. [全部的資源文件按照優先級的不一樣採用覆蓋的方式整合,product flavor覆蓋main,build type覆蓋product flavor] [?這裏的優先級總以爲有點問題?]

4 Each Build Variant generates its own R class (or other generated source code) from the resources. Nothing is shared between variants. [每一個Build Variant都會根據它的資源文件產生一個R清單類,而且在variants之間不進行共享]

5 Finally, like Build Types, Product Flavors can have their own dependencies. For instance, if the flavors are used to generate a ads-based app and a paid app, one of the flavors could have a dependency on an Ads SDK, while the other does not. [最後,build type和product flavor同樣均可以有本身的依賴項]

dependencies {
    flavor1Compile "..."
}

In this particular case, the file src/flavor1/AndroidManifest.xml would probably need to include the internet permission.

Additional sourcesets are also created for each variants:

android.sourceSets.flavor1Debug Location src/flavor1Debug/
android.sourceSets.flavor1Release Location src/flavor1Release/
android.sourceSets.flavor2Debug Location src/flavor2Debug/
android.sourceSets.flavor2Release Location src/flavor2Release/

These have higher priority than the build type sourcesets, and allow customization at the variant level.

[這些sourceSets的優先級比build type的sourceSets高,並且能夠在variant層進行自定義]

Building and Tasks

We previously saw that each Build Type creates its own assemble<name> task, but that Build Variants are a combination of Build Type and Product Flavor.

[當一個product flavor被使用時,更多的assemble類型的任務會被建立,它們分別針對了特定的variant或者build type或者flavor]

When Product Flavors are used, more assemble-type tasks are created. These are:

assemble<Variant Name>
assemble<Build Type Name>
assemble<Product Flavor Name>

1 allows directly building a single variant. For instance assembleFlavor1Debug.

2 allows building all APKs for a given Build Type. For instance assembleDebugwill build both Flavor1Debug and Flavor2Debug variants.

3 allows building all APKs for a given flavor. For instance assembleFlavor1 will build both Flavor1Debug and Flavor1Release variants.

The task assemble will build all possible variants.

Testing

[測試包含多個flavor 的項目]

Testing multi-flavors project is very similar to simpler projects.

The androidTest sourceset is used for common tests across all flavors, while each flavor can also have its own tests.

As mentioned above, sourceSets to test each flavor are created:

android.sourceSets.androidTestFlavor1 Location src/androidTestFlavor1/
android.sourceSets.androidTestFlavor2 Location src/androidTestFlavor2/

Similarly, those can have their own dependencies:

dependencies {
    androidTestFlavor1Compile "..."
}

Running the tests can be done through the main deviceCheck anchor task, or the main androidTest tasks which acts as an anchor task when flavors are used.

Each flavor has its own task to run tests: androidTest<VariantName>. For instance:

androidTestFlavor1Debug
androidTestFlavor2Debug

Similarly, test APK building tasks and install/uninstall tasks are per variant:

assembleFlavor1Test
installFlavor1Debug
installFlavor1Test
uninstallFlavor1Debug
...

Finally the HTML report generation supports aggregation by flavor.
The location of the test results and reports is as follows, first for the per flavor version, and then for the aggregated one:

build/androidTest-results/flavors/<FlavorName>
build/androidTest-results/all/
build/reports/androidTests/flavors<FlavorName>
build/reports/androidTests/all/

Customizing either path, will only change the root folder and still create sub folders per-flavor and aggregated results/reports.

Multi-flavor variants

[使用flavorGroups,此處有些複雜,若是有這種需求請細讀原文]

In some case, one may want to create several versions of the same apps based on more than one criteria.

For instance, multi-apk support in Google Play supports 4 different filters.

Creating different APKs split on each filter requires being able to use more than one dimension of Product Flavors.

Consider the example of a game that has a demo and a paid version and wants to use the ABI filter in the multi-apk support. With 3 ABIs and two versions of the application, 6 APKs needs to be generated (not counting the variants introduced by the different Build Types).

However, the code of the paid version is the same for all three ABIs, so creating simply 6 flavors is not the way to go.Instead, there are two dimensions of flavors, and variants should automatically build all possible combinations.

This feature is implemented using Flavor Groups. Each group represents a dimension, and flavors are assigned to a specific group.

android {
    ...

    flavorGroups "abi", "version"

    productFlavors {
        freeapp {
            flavorGroup "version"
            ...
        }

        x86 {
            flavorGroup "abi"
            ...
        }
    }
}

The android.flavorGroups array defines the possible groups, as well as the order. Each defined Product Flavor is assigned to a group.

From the following grouped Product Flavors [freeapp, paidapp] and [x86, arm, mips] and the [debug, release] Build Types, the following build variants will be created:

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

The order of the group as defined by android.flavorGroups is very important.

Each variant is configured by several Product Flavor objects:
android.defaultConfig
One from the abi group
One from the version group

The order of the group drives which flavor override the other, which is important for resources when a value in a flavor replaces a value defined in a lower priority flavor.

The flavor groups is defined with higher priority first. So in this case:
abi > version > defaultConfig

Multi-flavors projects also have additional sourcesets, similar to the variant sourcesets but without the build type:

android.sourceSets.x86Freeapp Location src/x86Freeapp/
android.sourceSets.armPaidapp Location src/armPaidapp/
etc...

These allow customization at the flavor-combination level. They have higher priority than the basic flavor sourcesets, but lower priority than the build type sourcesets.

Advanced Build Customization

Build options

Java Compilation options
android {
    compileOptions {
        sourceCompatibility = "1.6"
        targetCompatibility = "1.6"
    }
}

Default value is 「1.6」. This affect all tasks compiling Java source code.

aapt options
android {
    aaptOptions {
        noCompress 'foo', 'bar'
        ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
    }
}

This affects all tasks using aapt.

dex options
android {
    dexOptions {
        incremental false
        preDexLibraries = false
        jumboMode = false
    }
}

This affects all tasks using dex.

Manipulating tasks

[簡單的Java項目通常都是有限的任務一塊兒工做而後獲得一個輸出,例如classes 任務是用來編譯Java源代碼的任務,在build.gradle 文件中可使用classes 來引用]

Basic Java projects have a finite set of tasks that all work together to create an output.

The classes task is the one that compile the Java source code.
It’s easy to access from build.gradle by simply using classes in a script. This is a shortcut for project.tasks.classes.

[可是Android項目優勢複雜,由於它可能有不少相同的任務,這些任務的名稱是基於build type和product flavor來生成的]

In Android projects, this is a bit more complicated because there could be a large number of the same task and their name is generated based on the Build Types and Product Flavors.

In order to fix this, the android object has two properties:

applicationVariants (only for the app plugin)
libraryVariants (only for the library plugin)
testVariants (for both plugins)

All three return a DomainObjectCollection of ApplicationVariant, LibraryVariant, and TestVariant objects respectively.

Note that accessing any of these collections will trigger the creations of all the tasks. This means no (re)configuration should take place after accessing the collections.

The DomainObjectCollection gives access to all the objects directly, or through filters which can be convenient.

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

All three variant classes share the following properties:

image

The ApplicationVariant class adds the following:

image

The LibraryVariant class adds the following:

image

The TestVariant class adds the following:

image

API for Android specific task types.

image

The API for each task type is limited due to both how Gradle works and how the Android plugin sets them up.

1 First, Gradle is meant to have the tasks be only configured for input/output location and possible optional flags. So here, the tasks only define (some of) the inputs/outputs.

2 Second, the input for most of those tasks is non-trivial, often coming from mixing values from the sourceSets, the Build Types, and the Product Flavors. To keep build files simple to read and understand, the goal is to let developers modify the build by tweak these objects through the DSL, rather than diving deep in the inputs and options of the tasks and changing them.

Also note, that except for the ZipAlign task type, all other types require setting up private data to make them work. This means it’s not possible to manually create new tasks of these types.

[除了ZipAlign任務以外,其餘類型的任務都須要private data才能工做,因此沒有辦法manual建立這些類型的新任務]

This API is subject to change. In general the current API is around giving access to the outputs and inputs (when possible) of the tasks to add extra processing when required). Feedback is appreciated, especially around needs that may not have been foreseen.

For Gradle tasks (DefaultTask, JavaCompile, Copy, Zip), refer to the Gradle documentation.

BuildType and Product Flavor property reference

coming soon.

For Gradle tasks (DefaultTask, JavaCompile, Copy, Zip), refer to the Gradle documentation.

Using sourceCompatibility 1.7

[兼容JDK 1.7的方式,使用某些特性時還須要注意項目minSdkVersion 的配置]

With Android KitKat (buildToolsVersion 19) you can use the diamond operator, multi-catch, strings in switches, try with resources, etc. To do this, add the following to your build file:

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.0"

    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 19
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

Note that you can use minSdkVersion with a value earlier than 19, for all language features except try with resources. If you want to use try with resources, you will need to also use a minSdkVersion of 19.

You also need to make sure that Gradle is using version 1.7 or later of the JDK. (And version 0.6.1 or later of the Android Gradle plugin.)

相關文章
相關標籤/搜索