Gradle For Android(4)--構(gòu)建不同的版本

介紹

當(dāng)構(gòu)建App的時候,通常都會有不同的版本。比如說測試版本,正式版本,Debug版本等等。而這些版本通常有不同的配置,比如說服務(wù)器的域名,Log開關(guān),付費(fèi)開關(guān)等等特性。

之前我們看到了Release以及Debug版本的概念,而接下來會介紹product flavors的概念。而這也可以幫助我們管理不同的版本。Build TypeProduct Flavors總是聯(lián)合在一起的,它兩結(jié)合的結(jié)果就稱之為Build Variant。

Build Types

在Gradle的Android Plugin中,Build Type用于定義App以及Library如何構(gòu)建。每一個Build Type都會指明是否為Debug,Application Id,是否無用的資源應(yīng)該被刪除掉等等。你也可以在buildTypes的代碼塊中定義多種Build Types。Android Studio默認(rèn)生成的標(biāo)準(zhǔn)的build Types代碼塊如下:

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

一個新的Module默認(rèn)的build.gradle文件中會配置一個release的Build Type。這個build type通過設(shè)置minifyEnabled為false禁用刪除無用的Resources,以及定義了默認(rèn)的ProGuard配置文件。

創(chuàng)建Project的時候不僅僅只有Release的構(gòu)建類型,默認(rèn)每個Module都有一個Debug的構(gòu)建類型。我們可以在里面改改里面的值。

創(chuàng)建Build Type

當(dāng)默認(rèn)的配置不滿足需求時,我們可以創(chuàng)建我們自定義的Build Type。我們需要做的就是在buildTypes代碼塊中創(chuàng)建一個新的對象即可,如下所示,創(chuàng)建一個名為staging的Build Type:

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

staging的Build Type定義了一些Application Id的后綴,使得Application的ID與Debug/Release版本不一樣。假設(shè)你已經(jīng)有了默認(rèn)的Build配置,這些版本的ApplicationId會如下:

  • Debug: com.package
  • Release: com.package
  • Staging: com.package.staging

這也就意味著我們能夠在同一臺設(shè)備上安裝多個版本的。也可以使用buildConfigField屬性定義了不同的URL。

我們也可以通過Copy其他Build Type中的屬性,來初始化一個新的BuildType,通過initWith來初始化該BuildType對象。代碼如下所示:

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

initWith方法創(chuàng)建了一個新的Build Type,并且從一個已經(jīng)存在的build type中復(fù)制這些屬性。它也可以重寫這些屬性,或者定義其他的新的屬性。

Source sets

當(dāng)創(chuàng)建了一個新的build type之后,Gradle也會創(chuàng)建一個新的source set。默認(rèn)的source set目錄會放在相同的Build Type的目錄下。當(dāng)你創(chuàng)建一個新的build type時,該目錄不會自動創(chuàng)建,你必須在你使用代碼與資源前自己為每一個build type創(chuàng)建source set目錄。

這是標(biāo)準(zhǔn)的目錄結(jié)構(gòu),包含了三種Build Type:

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

比如,如果希望在某個build type下替換一些屬性,添加一些代碼,或者添加一些layouts、strings的話,都是可以做到的。

當(dāng)使用不同的source sets的時候,Resources會比較特殊。Drawables和layout文件都會被在Main Source Set中的相同名字的資源所重寫,但是在values文件夾下面的,如strings、colors、dimens等則不會。Gradle會用main resources來merge各個build type的資源。

例如,如果有一個strings.xml文件在main source set中:

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

如果在staging的build type中也存在一個strings.xml:

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

那么最后merge完的strings.xml會如下:

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

同樣AndroidManifest.xml也是同樣的,特定的build type的包會把main source set中的AndroidManifest.xml覆蓋。

Product flavors

Build Type可以對于相同的App配置生成不同類型的構(gòu)建,與Build Type相反,product flavors用來創(chuàng)建相同的App,但是不同的版本。典型的例子就是App有免費(fèi)和付費(fèi)版本。另外一個常用就是為只有一個品牌但是有很多客戶端,比如說滴滴,外賣,銀行等都有司機(jī)端和用戶端。他們只想修改Logo,Color,Url等等。Product Flavors可以很簡單的處理相同的代碼生產(chǎn)出不同的版本。

如果你不確定是否需要一個新的build type,或者新的product flavor,那么則需要看一下是否真的需要構(gòu)建一個新的APP發(fā)布到應(yīng)用市場上。

創(chuàng)建Product Flavors

我們可以通過添加productFlavor代碼塊來添加一個新的Product Flavor:

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

Product Flavors擁有和Build Type不同的屬性。因為Product Flavors是一個ProductFlavor類,就像defaultConfig對象一樣。這也就意味著,defaultConfig和所有的Product flavors共享相同的Properties。

Source Set

就像Build Types一樣,Product Flavors能夠擁有他們自己的Source Sets目錄。創(chuàng)建一個與Product Flavors名字相同的文件夾。而這個目錄的明哲,需要聯(lián)合它的Build Type以及Flavors,這樣用來覆蓋那些屬性。

比如,你想有一個不同的App Icon在blue flavors中生成一個Release版本的包,那么這個目錄應(yīng)該叫做blueRelease。然后這個組件所關(guān)聯(lián)的目錄將會比其他Build Type以及Product Flavors的組件目錄優(yōu)先級會更高。

Multiflavor variants

在某些情況下,你可能希望創(chuàng)建一些聯(lián)合的Product Flavors。比如說,Client A和Client B都基于相同的代碼需要一個免費(fèi)和付費(fèi)的版本。創(chuàng)建四個不同的Flavors單獨(dú)的Settings是不可行的。所以,Combining Flavors可以更高效的使用flavor dimensions

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

當(dāng)添加了flavor dimensions之后,Gradle希望你為每個Flavor都指定一個flavor dimension。如果你忘記了,則編譯時會報錯。flavorDimensions數(shù)組定義了這些Dimensions,而這些Dimensions的順序是非常重要的。當(dāng)需要聯(lián)合兩個Flavors的時候,你可能已經(jīng)定義了相同的Properties或者Resources。在這種情況下,flavors dimensions數(shù)組的順序決定了哪個flavor配置會覆蓋另外的。在之前的例子中,Color Dimension會覆蓋Price Dimension 。并且這個順序,也決定了構(gòu)建的名字。

假設(shè)默認(rèn)的構(gòu)建配置有Debug和Release兩種Build Type,就像之前的Example中定義的flavors就會生成以下這些版本:

  • blueFreeDebug and blueFreeRelease
  • bluePaidDebug and bluePaidRelease
  • redFreeDebug and redFreeRelease
  • redPaidDebug and redPaidRelease

Build variants

Build Variants僅僅只是Build Types以及Product Flavors的聯(lián)合。一旦創(chuàng)建了一個新的Build Type或者Product Flavor的話,那么一個新的Variants就會被創(chuàng)建。

例如,如果有一個標(biāo)準(zhǔn)的Debug和Release構(gòu)建類型,并且你創(chuàng)建了一個Red和Blue的Product Flavor,那么下面的Build Variant就會生成:

Build Variants

這是Android Studio中的一個窗口。可以在tool window的左下角找到它,或者從View->Tool Windows->Build Variants中打開。我們也可以選擇其中的Variant來執(zhí)行任務(wù)。如果沒有定義任何的Build Types的話,Android Plugin會默認(rèn)創(chuàng)建一個Debug的Build Type。

Tasks

Android Plugin會為每一個配置的Build Variant創(chuàng)建Tasks。一個新的Android App擁有Debug和Release兩種Build Types,所以默認(rèn)的就會有兩個Task,一個是assembleDebug一個是assembleRelease來構(gòu)建不同的APK。當(dāng)添加一個新的Build Type的時候,一個新的Task也就會被創(chuàng)建,一旦你開始添加Flavors,一整套Tasks就會被創(chuàng)建,因為每一個BuildType的Tasks都會為每個Product Flavor聯(lián)合。也就是,一個簡單的Build Type和Flavor設(shè)置后,就會有三個任務(wù)去構(gòu)建所有的Variants。

  • assembleBlue:使用blue flavor配置并且assemble BlueRelease和BlueDebug
  • assembleDebug:使用Debug Build Type的配置,并且為每一個Product Flavor assemble一個Debug的版本
  • assembleBlueDebug:combines特定的Flavor以及BuildType配置,并且Flavor的設(shè)置會覆蓋BuildType的設(shè)置

每一個BuildType和Product Flavor都會創(chuàng)建新的Tasks。

Source Set

Build Variants是一個聯(lián)合了BuildType和ProductFlavors并且使用它們自己SourceSet目錄的版本。

例如:Variant創(chuàng)建從Debug Build Type以及Blue、Free Flavor的版本,它可以擁有src/blueFreeDebug/java/的Source Set。我們可以在sourceSets代碼塊中重寫它的location。

Resource and manifest merging

Android Plugin需要在打包前對Main的SourceSet以及BuildType的SourceSet進(jìn)行一次Merge。而且Library工程也會提供額外的資源,它們也會被Merge,例如Manifest.xml等等。也會在其中聲明一些權(quán)限等。

Resource和Manifest.xml的優(yōu)先級順序如下:

Order.png

如果一個Resource聲明在Flavor和Main source set中的話,那么Flavor中的值優(yōu)先級會更高。在這種情況下,F(xiàn)lavor的SourceSet中的資源會被打包到APK中。而Library工程的資源優(yōu)先級會是最低的。

Creating build variants

Gradle可以很容易的處理復(fù)雜的多種構(gòu)建。甚至當(dāng)創(chuàng)建兩種BuildType和兩種Product Flavors的時候。例如:

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"
            } 
      }
}

在這個例子中,我們會創(chuàng)建出來四個不同版本的Variants:
blueDebug,blueStaging,redDebugredStaging
每一個都有API_URL以及flavor_color的屬性。

以下為blueDebug的樣式:

blueDebug

而以下為redStaging的樣式:

redStaging

Variant filters

通過Variant fileters的方式,可以完全忽略某種Variant的構(gòu)建,從而達(dá)到使用assemble命令的時候提升構(gòu)建的速度。并且不會執(zhí)行的Task也不會打印的Tasks列表中出現(xiàn)。這樣也同樣會確保build variant不會在Android Studio中顯示。

我們可以通過在App或者Library的Root-Level的build.gradle文件中添加以下代碼:

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

在這個例子中,首先檢查BuildType是否為Release,然后檢查Flavors的名字,如果flavors為blue則忽略。其中variant.getFlovors會獲取到flavor dimensions中所有的flavor。

variant filter

可以看到blueFreeReleasebluePaidRelease已經(jīng)不在列表中。如果直接執(zhí)行gradlew tasks的話,就會注意到所有和這個variants相關(guān)的tasks都不存在了。

Signing configurations

在發(fā)布App到Google Play或者其他的商店的時候,我們需要使用一個Private Key對APK進(jìn)行簽名。如果有一個付費(fèi)和免費(fèi)的版本,或者不同的客戶端版本時,你需要為不同的Flavor版本APK進(jìn)行不同的簽名。

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

在這個例子中,我們創(chuàng)建了兩個不同的簽名。
debug配置會被Android Plugin自動設(shè)置,并且使用一個已知的Password進(jìn)行簽名,所以不需要為Debug的BuildType創(chuàng)建簽名配置。而staging配置使用initWith,它是從另外一個簽名配置中Copy的屬性。這也就意味著staging的構(gòu)建會和Debug一樣的簽名,而沒有它自己定義的簽名。
release配置則使用storeFile來指定keystore文件,并且定義了Key的別名以及Password。

當(dāng)定義完了這個簽名的配置后,你需要在BuildType或者Flavors中應(yīng)用一下。BuildType和Flavors都有一個屬性叫做signingConfig,如下所示:

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

通過這種方式會對BuildType以及ProductFlavors應(yīng)用不同的簽名。

當(dāng)簽名一個Flavor版本的時候,你需要重寫B(tài)uildType中的簽名配置W。當(dāng)需要使用相同的BuildType不同版本的Flavors的簽名時,可以通過下述方式:

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

上面這個例子展示了如何在redblue的Release版本使用不同的簽名,但是卻不影響Debug和Staging的BuildType。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容