Androidアプリのビルド設定管理（Product Flavor、Build Type）

# Product FlavorとBuild Type

Android Gradle pluginには**Product Flavor**（以下Flavor）と**Build Type**という機能があり、テスト用、本番用などでアプリのビルド設定を切り替えることができる。
Flavorは主にアプリで使用する文字列や画像などのリソースの切り替え、Build Typeは署名や圧縮の設定切り替えをすることが多い。

Flavorは任意の名前のものを自分で定義するが、Build Typeは標準で**debug**と**release**
の2種類が用意されている。
Build Typeを追加することもできるが、特に必要なければdebugとreleaseをそのまま使用すればよい。

# Build Variants

FlavorとBuild Typeを組み合わせたものを**Build Variants**という。
例えば、Flavorをa、b、cの3種類定義し、Build Typeを標準のdebugとreleaseのままにした場合、Build Variantsは以下の6種類となる。

- aDebug
- aRelease
- bDebug
- bRelease
- cDebug
- cRelease

Android Studioでアプリをビルドする場合、Build Variantsのウィンドウで任意のBuild Variantを選択してビルドすることができる。

![スクリーンショット 2018-05-01 16.09.51.png](https://qiita-image-store.s3.amazonaws.com/0/233769/df549240-83b3-1cab-ec94-026be5138e3c.png)


# Variant Filter

Build VariantはFlavorとBuild Typeの全ての組み合わせが自動で作成されるが、**variantFilter**を用いて不要な組み合わせを作成しないようにできる。

```groovy:build.gradle

    variantFilter { variant ->
        // buildTypeが"debug"、flavorが"a"のBuild Variantは作成しない
        if (variant.buildType.name == "debug" && variant.flavors*.name.contains("a")) {
            setIgnore(true)
        }
    }
```

variantFilter内に任意の判定を書いて、`setIgnore(true)`とするとそのBuild Variantは作成されなくなる。

Flavorはvariant.flavorsの形でListで取得されるが、ここに記載するbuild.gradleではflavorsには一つのflavorしか入らない。

# FlavorおよびBuild Typeで切り替えられること

必要であれば様々な設定を切り替えられるが、主に以下を切り替えることが多い。

<dl>
<dt>applicationId</dt>
<dd>applicationIdをFlavor毎に変えることで、各Flavorでビルドしたアプリを別アプリとして同じ端末にインストールできる</dd>
<dt>文字列リソース</dt>
<dd>strings.xml などの文字列リソースをFlavorによって切り替えられる</dd>
<dt>プログラム処理</dt>
<dd>Flavorによってプログラム処理を切り替えられる</dd>
<dt>署名の設定(SigningConfig)</dt>
<dd>Build TypeまたはFlavorによってアプリの署名設定を切り替えられる</dd>
<dt>プロガード（難読化）の設定</dt>
<dd>Build TypeまたはFlavorによってプロガードの有効/無効などを切り替えられる</dd>
</dl>

# build.gradleの設定

以下はproduction、staging、develop３つのFlavorを定義して、それぞれapplicationIdを変更するbuild.gradleの例。

```groovy:build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "net.sample.myapplication"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }

    // !!!! ProductFlavorの設定 !!!!!
    flavorDimensions "default"
    productFlavors {
        production {
            applicationId "net.sample.myapplication"
        }
        staging {
            applicationId "net.sample.myapplication_staging"
        }
        develop {
            applicationId "net.sample.myapplication_develop"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
}
```

# リソースの切り替え

build.gradleにFlavorを定義すると、各Flavorで別々に文字列や画像などのリソースを保持することができる。（もちろん共有することも可能）

リソースをFlavor毎に定義する場合、"res/values" フォルダで右クリックし、コンテキストメニューから "New > Values resource file" を選択した後、"Source set"の欄で追加先のFlavorを選択してリソースファイルを追加する。（mainは全Flavorで共通）

![スクリーンショット 2018-05-01 14.28.17.png](https://qiita-image-store.s3.amazonaws.com/0/233769/a69c8cb8-90fa-4a0f-a634-cd86e8893686.png)

各ProductFlaovrに固有のリソースは、srcフォルダの下の各ProductFlaovr名のフォルダに配置される。

![スクリーンショット 2018-05-01 18.26.33.png](https://qiita-image-store.s3.amazonaws.com/0/233769/f8e16339-dd24-fef6-1dc7-241adbed01c6.png)

また、FlavorだけでなくBuild Typeごとにリソースを定義したり、FlavorとBuild Typeの組み合わせに対して定義することもできる。


## 文字列リソースの切り替え

Androidプロジェクトを作成すると標準でmain/resフォルダの下に文字列を定義するための strings.xml ファイルが用意されるが、Flaovr毎に文字列リソースを切り替える場合、これとは別に各Flavor用のxmlファイルを作成する。

ファイル名は`strings.xml`でも良いが、別の名前にしておくと共通の文字列リソースと区別がついて分かりやすい。
ここでは各Flavorごとの文字列リソースファイルを`environment.xml`として作成する前提で話を進める。


# アプリ名の切り替え

上記「リソースの切り替え」を用いて、ProductFlavor毎にアプリの表示名（ホーム画面のアプリアイコンの下に表示される名前）を変えることができる。

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.sample.myapplication">

    <application
        android:label="@string/app_name">
        <!-- 以下略 -->
    </application>

</manifest>
```

AndroidManifestに記載されたアプリ名は、上記のように標準で文字列リソースの`app_name`を参照するようになっているので、各Flavorの文字列リソースファイルに以下のように別々の`app_name`を記載してやれば、Flavor毎にアプリ名を切り替えることができる。

### Flavor developのenvironment.xml
```xml:environment.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">DevApp</string>
</resources>
```

### Flavor stagingのenvironment.xml
```xml:environment.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">STGApp</string>
</resources>
```

### Flavor productionのenvironment.xml
```xml:environment.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">本番アプリ名</string>
</resources>
```

# プログラム内での処理分岐方法

Flavorによってプログラム内の処理を分岐させることができる。
`BuildConfig.FLAVOR`から現在のFlavorを文字列で取得できるので、その値を見て処理を分岐させる。

```Java
public class Environment {
    String apiURL = null;

    public Environment() {
        if (BuildConfig.FLAVOR.equals("production")) {
            apiURL = "https://api-prod.net";
        } else if (BuildConfig.FLAVOR.equals("staging")) {
            apiURL = "https://api-stg.net";
        } else if (BuildConfig.FLAVOR.equals("develop")) {
            apiURL = "https://api-develop.net";
        }
    }
}
```

# signingConfigsの切り替え

signingConfigs（署名の設定）はFlavorやBuild Typeとは別に定義して、各FlavorやBuild Typeで任意のsigningConfigを使うことができる。

```groovy:build.gradle

// 複数のsigningConfigsを定義
signingConfigs {
    foo {
        storeFile = new File('keystore_foo.p12')
        storePassword = 'password'
        keyPassword = 'password'
        keyAlias = '1'
    }
    bar {
        storeFile = new File('keystore_bar.p12')
        storePassword = 'password'
        keyPassword = 'password'
        keyAlias = '1'
    }
}

buildTypes {
    release {
        // BuildType=releaseの場合、signingConfig fooを使用
        signingConfig signingConfigs.foo
    }
    debug {
        // BuildType=debugの場合、signingConfig barを使用
        signingConfig signingConfigs.bar
    }
}
```

上記の例はBuild TypeによってsigningConfigを切り替えているが、Flavorによって切り替えることもできる。

# カスタムフィールド（buildConfigField）の定義

**buildConfigField**を使って各Flavorごとに任意の値をプログラムに渡すことができる。

```groovy:build.gradle

productFlavors {
    a {
        applicationId "net.sample.myapplication"
        buildConfigField "boolean", "IS_LOG_ENABLED", "false"
    }
    b {
        applicationId "net.sample.myapplication_staging"
        buildConfigField "boolean", "IS_LOG_ENABLED", "true"
    }
    c {
        applicationId "net.sample.myapplication_develop"
        buildConfigField "boolean", "IS_LOG_ENABLED", "true"
    }
}
```

```Java:プログラム側処理
if (BuildConfig.IS_LOG_ENABLED) {
    Log.d("tag", "message");
}
```

Flavorによって挙動を変えたいが、BuildConfig.FLAVORの判定では分かりづらい場合などに用いる。
boolean以外にStringなども定義できるが、StringについてはFlavorごとに文字列リソースを用意できるので不要かと思われる。

# PUSH通知のパーミッション設定

プッシュ通知（GCM）を使う場合、AndroidManifest.xmlにカスタムのパーミッションを記載する必要があるが、どこかのAndroidバージョンからpermission名が重複するアプリがインストールできなくなってしまった。

そのため、複数Flavorのアプリを同時インストールしたい場合、パーミッション名を以下のように`applicationId`によって動的に切り替える。

```xml:AndroidManifest.xml

    <permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />

```