鍍金池/ 教程/ Android/ 構建變種版本 - Build Variants
依賴關系,Android 庫和多項目設置 - Dependencies,Android Libraries and Multi-
要求 - Requirements
構建變種版本 - Build Variants
高級構建定制 - Advanced Build Customization
測試 - Testing
基本項目 - Basic Project
簡介 - Introduction

構建變種版本 - Build Variants

新構建系統(tǒng)的一個目標就是允許為同一個應用創(chuàng)建不同的版本。

這里有兩個主要的使用情景:

  1. 同一個應用的不同版本。 例如一個免費的版本和一個收費的專業(yè)版本。
  2. 同一個應用需要打包成不同的apk以發(fā)布Google Play Store。 點擊此處 查看更多詳細信息。
  3. 綜合1和2兩種情景。

這個目標就是要讓在同一個項目里生成不同的APK成為可能,以取代以前需要使用一個庫項目和兩個及兩個以上的應用項目分別生成不同APK的做法。

不同定制的產品

一個product flavor定義了從項目中構建了一個應用的自定義版本。一個單一的項目可以同時定義多個不同的flavor來改變應用的輸出。

這個新的設計概念是為了解決不同的版本之間的差異非常小的情況。雖然最項目終生成了多個定制的版本,但是它們本質上都是同一個應用,那么這種做法可能是比使用庫項目更好的實現(xiàn)方式。

Product flavor需要在productFlavors這個DSL容器中聲明:

    android {
        ....

        productFlavors {
            flavor1 {
                ...
            }

            flavor2 {
                ...
            }
        }
    }

這里創(chuàng)建了兩個flavor,名為flavor1flavor2。

注意:
flavor的命名不能與已存在的_Build Type_或者androidTest這個_sourceSet_有沖突。

構建類型 + 定制產品 = 構建變種版本

正如前面章節(jié)所提到的,每一個_Build Type_都會生成一個新的APK。

_Product Flavor_同樣也會做這些事情:項目的輸出將會拼接所有可能的_Build Type_和Product Flavor(如果有Flavor定義存在的話)的組合。

每一種組合(包含_Build Type_和Product Flavor)就是一個Build Variant(構建變種版本)。

例如,在上面的Flavor聲明例子中與默認的debugrelease兩個_Build Type_將會生成4個Build Variant

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

項目中如果沒有定義flavor同樣也會有Build Variant,只是使用的是默認的flavor和配置。default(默認)的flavor/config是沒有名字的,所以生成的Build Variant列表看起來就跟_Build Type_列表一樣。

Product Flavor 的配置

每一個flavor都是通過閉包來配置的:

    android {
        ...

        defaultConfig {
            minSdkVersion 8
            versionCode 10
        }

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

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

注意_ProductFlavor_類型的android.productFlavors.*對象與android.defaultConfig對象的類型是相同的。這意味著它們共享相同的屬性。

defaultConfig為所有的flavor提供基本的配置,每一個flavor都可以重設這些配置的值。在上面的例子中,最終的配置結果將會是:

* `flavor1`
    * `packageName`: com.example.flavor1
    * `minSdkVersion`: 8
    * `versionCode`: 20
* `flavor2`
    * `packageName`: com.example.flavor2
    * `minSdkVersion`: 14
    * `versionCode`: 10

通常情況下,_Build Type_的配置會覆蓋其它的配置。例如,_Build Type_的packageNameSuffix會被追加到_Product Flavor_的packageName上面。

也有一些情況是一些設置可以同時在_Build Type_和_Product Flavor_中設置。在這種情況下,按照個別為主的原則決定。

例如,signingConfig就這種屬性的一個例子。 signingConfig允許通過設置android.buildTypes.release.signingConfig來為所有的release包共享相同的SigningConfig。也可以通過設置android.productFlavors.*.signingConfig來為每一個release包指定它們自己的SigningConfig。

源組件和依賴關系

與_Build Type_類似,_Product Flavor_也會通過它們自己的_sourceSet_提供代碼和資源。

上面的例子將會創(chuàng)建4個sourceSet

  • android.sourceSets.flavor1 位于src/flavor1/
  • android.sourceSets.flavor2 位于src/flavor2/
  • android.sourceSets.androidTestFlavor1 位于src/androidTestFlavor1/
  • android.sourceSets.androidTestFlavor2 位于src/androidTestFlavor2/

這些_sourceSet_用于與android.sourceSets.main和_Build Type_的_sourceSet_來構建APK。

下面的規(guī)則用于處理所有使用的sourceSet來構建一個APK:

  • 多個文件夾中的所有的源代碼(src/*/java)都會合并起來生成一個輸出。
  • 所有的Manifest文件都會合并成一個Manifest文件。類似于Build Type,允許_Product Flavor_可以擁有不同的的組件和權限聲明。
  • 所有使用的資源(Android res和assets)遵循的優(yōu)先級為_Build Type_會覆蓋Product Flavor,最終覆蓋main _sourceSet_的資源。
  • 每一個_Build Variant_都會根據(jù)資源生成自己的R類(或者其它一些源代碼)。Variant互相之間沒有什么是共享的。

最終,類似Build Type,_Product Flavor_也可以有它們自己的依賴關系。例如,如果使用flavor來生成一個基于廣告的應用版本和一個付費的應用版本,其中廣告版本可能需要依賴于一個廣告SDK,但是另一個不需要。

dependencies {
    flavor1Compile "..."
}

在這個例子中,src/flavor1/AndroidManifest.xml文件中可能需要聲明訪問網絡的權限。

每一個Variant也會創(chuàng)建額外的sourceSet:

  • android.sourceSets.flavor1Debug 位于src/flavor1Debug/
  • android.sourceSets.flavor1Release 位于src/flavor1Release/
  • android.sourceSets.flavor2Debug 位于src/flavor2Debug/
  • android.sourceSets.flavor2Release 位于src/flavor2Release/

這些sourceSet擁有比Build Type的sourceSet更高的優(yōu)先級,并允許在Variant的層次上做一些定制。

構建和任務

我們前面提到每一個_Build Type_會創(chuàng)建自己的assemble< name >task,但是_Build Variant_是_Build Type_和_Product Flavor_的組合。

當使用_Product Flavor_的時候,將會創(chuàng)建更多的assemble-type task。分別是:

  1. assemble< Variant Name > 允許直接構建一個Variant版本,例如assembleFlavor1Debug。
  2. assemble< Build Type Name > 允許構建指定Build Type的所有APK,例如assembleDebug將會構建Flavor1Debug和Flavor2Debug兩個Variant版本。
  3. assemble< Product Flavor Name > 允許構建指定flavor的所有APK,例如assembleFlavor1將會構建Flavor1Debug和Flavor1Release兩個Variant版本。

另外assemble task會構建所有可能組合的Variant版本。

測試

測試multi-flavors項目非常類似于測試簡單的項目。

androidTest _sourceSet_用于定義所有flavor共用的測試,但是每一個flavor也可以有它自己特有的測試。

正如前面提到的,每一個flavor都會創(chuàng)建自己的測試sourceSet:

  • android.sourceSets.androidTestFlavor1 位于src/androidTestFlavor1/
  • android.sourceSets.androidTestFlavor2 位于src/androidTestFlavor2/

同樣的,它們也可以擁有自己的依賴關系:

    dependencies {
        androidTestFlavor1Compile "..."
    }

這些測試可以通過main的標志性deviceCheck task或者main的androidTest task(當flavor被使用的時候這個task相當于一個標志性task)來執(zhí)行。

每一個flavor也擁有它們自己的task來這行這些測試:androidTest< VariantName >。例如:

  • androidTestFlavor1Debug
  • androidTestFlavor2Debug

同樣的,每一個Variant版本也會創(chuàng)建對應的測試APK構建task和安裝或卸載task:

  • assembleFlavor1Test
  • installFlavor1Debug
  • installFlavor1Test
  • uninstallFlavor1Debug
  • ...

最終的HTML報告支持根據(jù)flavor合并生成。 下面是測試結果和報告文件的路徑,第一個是每一個flavor版本的結果,后面的是合并起來的結果:

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

Multi-flavor variants

在一些情況下,一個應用可能需要基于多個標準來創(chuàng)建多個版本。例如,Google Play中的multi-apk支持4個不同的過濾器。區(qū)分創(chuàng)建的不同APK的每一個過濾器要求能夠使用多維的Product Flavor。

假如有個游戲需要一個免費版本和一個付費的版本,并且需要在multi-apk支持中使用ABI過濾器(譯注:ABI,應用二進制接口,優(yōu)點是不需要改動應用的任何代碼就能夠將應用遷移到任何支持相同ABI的平臺上)。這個游戲應用需要3個ABI和兩個特定應用版本,因此就需要生成6個APK(沒有因計算不同_Build Types_生成的Variant版本)。 然而,注意到在這個例子中,為三個ABI構建的付費版本源代碼都是相同,因此創(chuàng)建6個flavor來實現(xiàn)不是一個好辦法。 相反的,使用兩個flavor維度,并且自動構建所有可能的Variant組合。

這個功能的實現(xiàn)就是使用Flavor Groups。每一個Group代表一個維度,并且flavor都被分配到一個指定的Group中。

    android {
        ...

        flavorGroups "abi", "version"

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

            x86 {
                flavorGroup "abi"
                ...
            }

            ...
        }
    }

andorid.flavorGroups數(shù)組按照先后排序定義了可能使用的group。每一個_Product Flavor_都被分配到一個group中。

上面的例子中將_Product Flavor_分為兩組(即兩個維度),為別為abi維度[x86,arm,mips]和version維度[freeapp,paidapp],再加上默認的_Build Type_有[debug,release],這將會組合生成以下的Build Variant:

  • 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.flavorGroups中定義的group排序非常重要(Variant命名和優(yōu)先級等)。

每一個Variant版本的配置由幾個Product Flavor對象決定:

  • android.defaultConfig
  • 一個來自abi組中的對象
  • 一個來自version組中的對象

flavorGroups中的排序決定了哪一個flavor覆蓋哪一個,這對于資源來說非常重要,因為一個flavor中的值會替換定義在低優(yōu)先級的flavor中的值。

flavor groups使用最高的優(yōu)先級定義,因此在上面例子中的優(yōu)先級為:

abi > version > defaultConfig

Multi-flavors項目同樣擁有額外的sourceSet,類似于Variant的sourceSet,只是少了Build Type:

  • android.sourceSets.x86Freeapp 位于src/x86Freeapp/
  • android.sourceSets.armPaidapp 位于src/armPaidapp/
  • 等等...

這允許在flavor-combination的層次上進行定制。它們擁有過比基礎的flavor sourceSet更高的優(yōu)先級,但是優(yōu)先級低于Build Type的sourceSet。