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

Product FlavorとBuild Type

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

Flavorは任意の名前のものを自分で定義するが、Build Typeは標準でdebugrelease
の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

Variant Filter

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

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で切り替えられること

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

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

build.gradleの設定

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

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

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

スクリーンショット 2018-05-01 18.26.33.png

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

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

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

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

アプリ名の切り替え

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

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

environment.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">DevApp</string>
</resources>

Flavor stagingのenvironment.xml

environment.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">STGApp</string>
</resources>

Flavor productionのenvironment.xml

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

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

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

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を使うことができる。

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ごとに任意の値をプログラムに渡すことができる。

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"
    }
}
プログラム側処理
if (BuildConfig.IS_LOG_ENABLED) {
    Log.d("tag", "message");
}

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

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

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

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

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.