Gradle for Android 第四篇( 構建變體 )

這一系列暫不更新,相關技術討論,請移步微信羣,謝謝,但願你們多多支持!java

圖片描述

這是一個系列,因此若是你看完這篇文章,請看下列文章:android

Gradle for Android 第一篇( 從 Gradle 和 AS 開始 )segmentfault

Gradle for Android 第二篇( Build.gradle入門 )api

Gradle for Android 第三篇( 依賴管理 )微信

Gradle for Android 第五篇( 多模塊構建 )app

Gradle for Android 第六篇( 測試)框架

Gradle for Android 第七篇( Groovy入門 )ide

當你在開發一個app,一般你會有幾個版本。大多數狀況是你須要一個開發版本,用來測試app和弄清它的質量,而後還須要一個生產版本。這些版本一般有不一樣的設置,例如不一樣的URL地址。更可能的是你可能須要一個免費版和收費版本。基於上述狀況,你須要處理不一樣的版本:開發免費版,開發付費版本,生產免費版,生產付費版,而針對不一樣的版本不一樣的配置,這極大增長的管理難度。工具

Gradle有一些方便的方法來管理這些問題。咱們很早以前談過debug和release版本,如今咱們談到另一個概念,不一樣的產品版本。構建版本和生產版本一般能夠合併,構建版本和生產版本的合併版叫作構建變種。學習

這一章咱們將學習構建版本,它能使得開發更有效率,而且學習如何使用它們。而後咱們會討論構建版本和生產版本的不一樣,以及如何將其合併。咱們會探討簽名機制,如何針對不一樣的變種簽名等。

在這一章,咱們遵循以下規則:

  • Build types

  • Product flavors

  • Build variants

  • Signing configurations

構建版本

在Gradle的Android插件中,一個構建版本意味着定義一個app或者依賴庫如何被構建。每一個構建版本都要特殊的一面,好比是否須要debug,application id是什麼,是否不須要的資源被刪除等等。你能夠定義一個構建版本經過buildTypes方法。例如:

android {
       buildTypes {
           release {
               minifyEnabled false
               proguardFiles getDefaultProguardFile
                 ('proguard-android.txt'), 'proguard-rules.pro'
           }
        }
 }

這個文件定義了該模塊是release版本,而後定義了proguard的位置。該release版本不是惟一的構建版本,默認狀況下,還有個debug版本。Android studio把它視爲默認構建版本。

建立本身的構建版本

當默認的構建版本不夠用的時候,建立版本也是很容易的一件事,建立構建版本你只須要在buildTypes寫入本身的版本。以下所示:

android {
    buildTypes {
        staging {
            applicationIdSuffix ".staging"
            versionNameSuffix "-staging"
            buildConfigField "String", "API_URL",
            "\"http://staging.example.com/api\""
         }
    }
}

咱們定義了一個staging版本,該版本定義了一個新的application id,這讓其與debug和release版本的applicationID不一樣。假設你使用了默認的配置,那麼applicationID將會是這樣的:

  • Debug: com.package

  • Release: com.package

  • Staging: com.package.staging

這意味着你能夠在你的設備上安裝staging版本和release版本。staging版本也有本身的版本號。buildConfigField定義了一個新的URL地址。你沒必要事事都去建立,因此最可能的方式是去繼承已有的版本。

android {
       buildTypes {
           staging.initWith(buildTypes.debug)
           staging {
               applicationIdSuffix ".staging"
               versionNameSuffix "-staging"
               debuggable = false
           } 
        }
}

initWith()方法建立了一個新的版本的同時,複製全部存在的構建版本,相似繼承。咱們也能夠複寫該存在版本的全部屬性。

Source sets

當你建立了一個新的構建版本,Gradle也建立了新的source set。默認狀況下,該文件夾不會自動爲你建立,全部你須要手工建立。

app
└── src
├── debug
│ ├── java
       │   │   └── com.package
 │ │
│ ├── res
│ │ └── layout
│   │       └── activity_main.xml
│   └── AndroidManifest.xml
├── main
│ ├── java
│   │   └── com.package
│ │
│ ├── res
└── MainActivity.java
└── Constants.java
│ │
│ │
│ │
│   └── AndroidManifest.xml
├── staging
│ ├── java
│   │   └── com.package
├── drawable
└── layout
└── activity_main.xml
│ │
│ ├── res
│ │ └── layout
│   │       └── activity_main.xml
│   └── AndroidManifest.xml
└── release
    ├── java
    │   └── com.package
    │       └── Constants.java
    └── AndroidManifest.xml

注意:當你添加一個Java類的時候,你須要知道如下過程,當你添加了CustomLogic.java到staging版本,你能夠添加相同的類到debug和release版本,可是不能添加到main版本。若是你添加了,會拋出異常。

當使用不一樣的source sets的時候,資源文件的處理須要特殊的方式。Drawables和layout文件將會複寫在main中的重名文件,可是values文件下的資源不會。gradle將會把這些資源連同main裏面的資源一塊兒合併。

舉個例子,當你在main中建立了一個srings.xml的時候:

<resources>
       <string name="app_name">TypesAndFlavors</string>
       <string name="hello_world">Hello world!</string>
</resources>

當你在你的staing版本也添加了rings.xml:

<resources>
       <string name="app_name">TypesAndFlavors STAGING</string>
</resources>

而後合併的strings.xml將會是這樣的:

<resources>
       <string name="app_name">TypesAndFlavors STAGING</string>
       <string name="hello_world">Hello world!</string>
</resources>

當你建立一個新的構建版本而不是staging,最終的strings.xml將會是main目錄下的strings.xml。

manifest也和value文件下的文件同樣。若是你爲你的構建版本建立了一個manifest文件,那麼你沒必要要去拷貝在main文件下的manifest文件,你須要作的是添加標籤。Android插件將會爲你合併它們。

咱們將在會以後的章節講到合併的更多細節。

依賴包

每個構建版本都有本身的依賴包,gradle自動爲每個構建的版本建立不一樣的依賴配置。若是你想爲debug版本添加一個logging框架,你能夠這麼作:

dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])
   compile 'com.android.support:appcompat-v7:22.2.0'
   debugCompile 'de.mindpipe.android:android-logging-log4j:1.0.3'
}

你能夠結合不一樣的構建版本着不一樣的構建配置,就像這種方式,這讓你的不一樣版本的不一樣依賴包成爲可能。

product flavors

和構建版本不一樣,product flavors用來爲一個app建立不一樣版本。典型的例子是,一個app有付費和免費版。product flavors極大簡化了基於相同的代碼構建不一樣版本的app。

若是你不肯定你是否須要一個新的構建版本或者product flavors,你應該問你本身,你是否須要內部使用和外部使用的apk。若是你須要一個徹底新的app去發佈,和以前的版本徹底隔離開,那麼你須要product flavors。不然你只是須要構建版本。

建立product flavors

建立product flavors很是的容易。你能夠在productFlavors中添加代碼:

android {
    productFlavors {
        red {
             applicationId 'com.gradleforandroid.red'
             versionCode 3
        }
        blue {
             applicationId 'com.gradleforandroid.blue'
             minSdkVersion 14
             versionCode 4
        }
    }
}

product flavors和構建版本的配置不一樣。由於product flavors有本身的ProductFlavor類,就像defaultConfig,這意味着你的全部productFlavors都分享同樣的屬性。

Source sets

就像構建版本同樣,product Flavors也有本身的代碼文件夾。建立一個特殊的版本就像建立一個文件夾那麼簡單。舉個例子,當你有的生產版本的blue flavors有一個不一樣的app圖標,該文件夾須要被叫作blueRelease。

多個flavors構建變體

在一些例子中,你可能須要建立一些product flavors的合併版本。舉個例子,client A和client B可能都想要一個free和paid的版本,而他們又都是基於同樣的代碼,可是有不同的顏色等。建立四個不一樣的flavors意味着有重複的配置。合併flavors最簡單的作法多是使用flavor dimensions,就像這樣:

android {
       flavorDimensions "color", "price"
       productFlavors {
           red {
               flavorDimension "color"
           }
           blue {
               flavorDimension "color"
           }
           free {
               flavorDimension "price"
           }
           paid {
               flavorDimension "price"
           }
       }
}

當你添加了flavor dimensions,你就須要爲每一個flavor添加flavorDimension,不然會提示錯誤。flavorDimensions定義了不一樣的dimensions,固然其順序也很重要。當你合併二個不一樣的flavors時,他們可能有同樣的配置和資源。例如上例:

  • blueFreeDebug and blueFreeRelease

  • bluePaidDebug and bluePaidRelease

  • redFreeDebug and redFreeRelease

  • redPaidDebug and redPaidRelease

構建變體

構建變體是構建版本和生產版本的結合體。當你建立了一個構建版本或者生產版本,一樣的,新的變體也會被建立。舉個例子,當你有debug和release版本,你建立了red和blue的生產版本,那麼變體將會有四個:

圖片描述

你能夠在Android studio的左下角找到它,或者經過VIEW|Tool Windows|Build Variants打開它。該視圖列出了全部的變體,而且容許你去切換它們。改變他們將會影響到你按Run按鈕。

若是你沒有product flavors,那麼變體只是簡單的包含構建版本,就算你沒有定義任何構建版本,Android studio也會默認爲你建立debug版本的。

tasks

android插件回味每個變體建立不一樣的配置。一個新的Android項目會有debug和release版本,全部你可使用assembleDebug和assembleRelease,固然當你使用assemble命令,會兩者都執行。當你添加了一個新的構建版本,新的task也會被建立。例如:

  • assembleBlue uses the blue flavor configuration and assembles both BlueRelease and BlueDebug.

  • assembleBlueDebug combines the flavor configuration with the build type configuration, and the flavor settings override the build type settings.

Source sets

構建變體也能夠有本身的資源文件夾,舉個例子,你能夠有src/blueFreeDebug/java/。

資源文件和manifest的合併

在打包app以前,Android插件會合並main中的代碼和構建的代碼。固然,依賴項目也能夠提供額外的資源,它們也會被合併。你可能須要額外的Android權限針對debug變體。舉個例子,你不想在main中申明這個權限,由於這可能致使一些問題,因此你能夠添加一個額外的mainfest文件在debug的文件夾中,申明額外的權限。

資源和mainfests的優先級是這樣的:

圖片描述

若是一個資源在main中和在flavor中定義了,那麼那個在flavor中的資源有更高的優先級。這樣那個在flavor文件夾中的資源將會被打包到apk。而在依賴項目申明的資源老是擁有最低優先級。

建立構建變體

gradle讓處理構建變體變得容易。

android {
       buildTypes {
           debug {
               buildConfigField "String", "API_URL",
               "\"http://test.example.com/api\""
        }
           staging.initWith(android.buildTypes.debug)
           staging {
               buildConfigField "String", "API_URL",
                 "\"http://staging.example.com/api\""
               applicationIdSuffix ".staging"
           }
       }
       productFlavors {
           red {
               applicationId "com.gradleforandroid.red"
               resValue "color", "flavor_color", "#ff0000"
           }
           blue {
               applicationId "com.gradleforandroid.blue"
               resValue "color", "flavor_color", "#0000ff"
           } 
     }
}

在這個例子中,咱們建立了4個變體,分別是blueDebug,blueStaging,redDebug,redStaging。每個變體都有其不一樣的api url以及顏色。例如:

圖片描述

圖片描述

變體過濾器

忽略某個變體也是可行的。這樣你能夠加速你的構建當使用assemble的時候,這樣你列出的tasks將不會執行那麼你不須要的變體。你可使用過濾器,在build.gradle中添加代碼以下所示:

android.variantFilter { variant ->
       if(variant.buildType.name.equals('release')) {
           variant.getFlavors().each() { flavor ->
               if (flavor.name.equals('blue')) { variant.setIgnore(true);
            }
        }
    }
}

在這個例子中,咱們檢查下:

圖片描述

你能夠看到blueFreeRelease和bluePaidRelease被排除在外,若是你運行gradlew tasks,你會發現全部的關於上述變體的tasks再也不存在。

簽名配置

在你發佈你的應用以前,你須要爲你的app私鑰簽名。若是你有付費版和免費版,你須要有不一樣的key去簽名不一樣的變體。這就是配置簽名的好處。配置簽名能夠這樣定義:

android {
       signingConfigs {
           staging.initWith(signingConfigs.debug)
           release {
               storeFile file("release.keystore")
               storePassword"secretpassword"
               keyAlias "gradleforandroid"
               keyPassword "secretpassword"
           }
      }
}

在這個例子中,咱們建立了2個不一樣的簽名配置。debug配置是as默認的,其使用了公共的keystore和password,因此沒有必要爲debug版本建立簽名配置了。staging配置使用了initWith()方法,其會複製其餘的簽名配置。這意味着staging和debug的key是同樣的。

release配置使用了storeFile,定義了key alias和密碼。固然這不是一個好的選擇,你須要在 Gradle properties文件中配置。

當你定義了簽名配置後,你須要應用它們。構建版本都有一個屬性叫作signingConfig,你能夠這麼幹:

android {
       buildTypes {
           release {
               signingConfig signingConfigs.release
           } 
       }
}

上例使用了buildTypes,可是你可能須要對每一個版本生成不一樣的驗證,你能夠這麼定義:

android {
       productFlavors {
           blue {
               signingConfig signingConfigs.release
           }
       }
}

固然,你在flavor中定義這些,最好會被重寫,因此最好的作法是:

android {
       buildTypes {
           release {
               productFlavors.red.signingConfig signingConfigs.red
               productFlavors.blue.signingConfig signingConfigs.blue
           }
       }
}

總結

在這一章,咱們討論了構建版本和生產版本,以及如何結合它們。這將會是很是有用的工具,當你須要不一樣的url以及不一樣的keys,而大家有相同的代碼和資源文件,可是有不一樣的類型以及版本,構建版本和生產版本將會讓你的生活更美好。

咱們也談論了簽名配置以及如何使用他們。

下一章,你將會學習到多模塊構建,由於當你想把你的代碼分紅一個依賴包或者依賴項目的時候,或者你想把Android wear模塊放在你的應用的時候,這將很是重要。

相關文章
相關標籤/搜索