Product Flavorとは何か、Gradleでの設定と例

著者: IT Sectr 公開日: 2026-05-30 読了時間: 9 分

Android開発におけるProduct Flavorは、共有コードベースから同じアプリケーションの複数のバリアントを作成できるGradleのメカニズムです。各flavorは独自のapplicationId、リソース、依存関係、機能を持つことができます(例:無料版と有料版)。Google Android Developers、2025年によると、Product FlavorsはBuild Variantsシステムの一部であり、flavorDimensionsを介してBuild Typesと組み合わされます。これはGoogle Playで複数のアプリバージョンを公開するための標準的なアプローチです。

重要なポイント

  • Product Flavor — 一意のapplicationId、リソース、コードを持つプロダクトバリアント。
  • Flavor Dimensionsは多次元設定のためflavorを独立した軸にグループ化します。
  • Source setsはflavorのメインリソース(アイコン、文字列、マニフェスト)を上書きします。
  • Gradleはflavor + build typeの各組み合わせに対して自動的にBuild Variantを生成します。
  • Google Playは複数のflavorを別々のアプリとして、または異なる設定を持つ単一アプリとして公開することをサポートしています。

Product Flavorとは?

Product Flavorはandroid.productFlavorsブロック内のGradle設定で、プロダクトのバリアントを記述します。各flavorはapplicationId、versionName、versionCode、minSdkVersion、targetSdkVersion、signingConfig、およびdefaultConfigのその他のパラメータを上書きできます。Product Flavorsに数量制限はありません。プロジェクトには2、5、10個のflavorを含めることができ、Gradleがすべての組み合わせを処理します。

Product Flavorはコードベースの再利用(codebase reuse)の問題を解決します。つまり、単一のリポジトリから複数の異なるアプリケーションをビルドする必要がある場合です。典型的なシナリオ:広告付きの無料版と広告なしの有料版、機能制限付きのデモ版、企業向けと消費者向けのバージョン、さまざまなクライアント向けのホワイトラベルアプリ。Product Flavorsがない場合、各バージョンを別々のプロジェクトで維持する必要があり、60〜70%のコード重複が発生します。

歴史的に、Product FlavorsはAndroid Gradle Plugin 0.9(2013年)でant設定の置き換えとして登場しました。それ以前は、開発者は異なるバージョンに別々のプロジェクトを使用するか、ビルド前に手動でリソースを置き換えていました。AGPへのflavorの導入により、アプローチが統一され、標準になりました。JetBrains、2024年の調査によると、複数バージョンを持つAndroidプロジェクトの78%がProduct Flavorsを使用しており、残りはBuildConfigやリフレクションを介した手動切り替えを使用しています。

Product Flavor vs Build Type

Build Typeはビルドプロセス(デバッグ付きのdebug、最適化付きのrelease)を管理します。Product Flavorはビルドコンテンツ(有料機能なしのfree、ありのpaid)を管理します。Build Typeはインフラストラクチャ設定であり、Product Flavorはプロダクト設定です。両方の概念は直交しています。free flavorのdebugビルドは、free flavorのreleaseビルドとはコンパイルパラメータのみが異なり、機能は異なりません。Product Flavorを使用してデバッガを無効にすることはできません。それはBuild Typeの役割です。

Flavor Dimensions:次元の整理

次元の順序と優先順位

Flavor DimensionsはProduct Flavorsを独立したカテゴリにグループ化するメカニズムです。アプリに無料版/有料版と、別にアメリカ/ヨーロッパ地域がある場合、flavorは2つの次元にグループ化されます:「tier」(free、paid)と「region」(us、eu)。Gradleは次元のデカルト積を作成します:freeUs、freeEu、paidUs、paidEu — 4つのバリアント。次元がない場合、Gradleは4つすべてのflavorを単一の平面として扱い、1つしか選択できません。

次元はflavorDimensionsブロックで文字列または文字列リストとして宣言されます。次元の順序はsource setの優先順位に影響します。最初の次元が最も高い優先順位を持ちます。次元A(tier)が最初に指定された場合、リソースの競合が発生したときにsrc/free/src/us/を上書きします。順序はVariant名の形成方法にも影響します。最初に最初の次元のflavor、次に2番目の次元、次にBuild Typeが来ます:freeUsDebug。

次元の数に制限はありませんが、新しい次元ごとにBuild Variantsの数が乗算されます。4つの次元(それぞれ2つのflavor)と2つのbuild typesを持つプロジェクトの場合、2 × 2 × 2 × 2 × 2 = 32のバリアントになります。実用的な制限は3次元(最大8〜12バリアント)です。それを超えると、Gradle設定が遅くなり、Android StudioのBuild Variantsパネルが読みにくくなります。

groovy
android {
    flavorDimensions "tier", "api"

    productFlavors {
        free {
            dimension "tier"
            applicationId "com.example.app.free"
            versionNameSuffix "-free"
        }
        paid {
            dimension "tier"
            applicationId "com.example.app.paid"
        }
        minApi21 {
            dimension "api"
            minSdk 21
        }
        minApi26 {
            dimension "api"
            minSdk 26
        }
    }
}

// 結果:freeMinApi21、freeMinApi26、paidMinApi21、paidMinApi26
// 各×debug/release = 8つのBuild Variants

build.gradleでのProduct Flavorsの作成

Product FlavorsのためのKotlin DSL

Product Flavorを作成するには、android内にproductFlavorsブロックを追加し、flavor名とそのパラメータを指定する必要があります。最小限のflavor宣言は名前と次元です。その他のすべてのパラメータはdefaultConfigから継承され、上書きできます。flavorはapplicationId、versionCode、testInstrumentationRunnerを含むdefaultConfigを完全に継承します。

各flavorはapplicationIdを上書きできます。これにより、同じデバイスに複数のアプリバージョンを同時にインストールできます。たとえば、無料版はcom.example.app.free、有料版はcom.example.app.paidになります。applicationIdが上書きされない場合、すべてのflavorは同じ識別子を持ち、並行してインストールできません。applicationIdはマニフェストのパッケージと一致する必要があります(applicationIdSuffixが使用されていない場合)。

AGP 8+はbuild.gradleにGroovyではなくKotlin DSLを使用することを推奨しています。Kotlin DSLは設定への型安全なアクセスを提供します。IDEがパラメータ名を提案し、コンパイル時に型をチェックし、エラーを強調表示します。Product FlavorsのためのGroovyからKotlin DSLへの移行は、通常、引用符を括弧に置き換え、型を追加することで構成されます。AGPは下位互換性があります。両方の構文が同じプロジェクト内で並行して機能します。

kotlin
// build.gradle.kts — Kotlin DSL
android {
    flavorDimensions += "tier"

    productFlavors {
        register("free") {
            dimension = "tier"
            applicationId = "com.example.app.free"
            versionNameSuffix = "-free"
            buildConfigField("boolean", "IS_PREMIUM", "false")
        }
        register("paid") {
            dimension = "tier"
            applicationId = "com.example.app.paid"
            versionNameSuffix = "-paid"
            buildConfigField("boolean", "IS_PREMIUM", "true")
        }
    }
}

異なるflavor向けのリソースとコード

各Product Flavorは独自のsource setsrc/<flavorName>/ディレクトリを作成します。このディレクトリには、上書きされたリソース、ソースファイル、マニフェストを含めることができます。flavor source setはmainの上にオーバーレイとして機能します。src/free/res/のファイルは、同じ名前のsrc/main/res/のファイルを上書きします。これにより、メインコードを変更せずに、flavorごとに異なる文字列、アイコン、色、レイアウトを持つことができます。

Java/Kotlinクラスを上書きするには、flavor固有の実装(各flavorで抽象クラスを実装する)とBuildConfigフィールド(コードでの分岐)の2つのアプローチがあります。最初のアプローチの方がクリーンです。mainでインターフェースまたは抽象クラスを定義し、src/free/とsrc/paid/に具体的な実装を配置します。ビルド時には、現在のflavorの実装のみがコンパイルされます。これにより、APKサイズの削減(有料コードが無料版に入らない)とセキュリティ(誤って有料関数を呼び出すことが不可能)という同時の利点が得られます。

flavor source setのAndroidManifest.xmlは置き換えではなく、メインマニフェストとマージされます。マージはAndroidのルールに従います。同じ要素内の重複する属性は上書きされ、一意の属性は追加されます。たとえば、メインマニフェストがINTERNET権限を宣言し、freeが宣言しない場合、インターネット権限は残ります。ただし、tools:node="replace"を使用すると、特定のflavorのマニフェストブロック全体を置き換えることができます。これは、異なるflavorが異なる権限(有料版のSDカード書き込み、無料版のカメラ)を必要とする場合に役立ちます。

xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:label="Free App"
        tools:replace="android:label">
    </application>
</manifest>

例:アプリの無料版と有料版

典型的なシナリオを考えてみましょう。free — 広告と基本機能を備えたバージョン、paid — 広告なし、拡張機能を備えたバージョン。無料版の場合、applicationIdは「com.example.app.free」に設定され、有料版は「com.example.app.paid」に設定されます。applicationIdはAndroidシステムにおけるアプリケーションの一意識別子であるため、両方のバージョンを同じデバイスに同時にインストールできます。

アーキテクチャ的には、分離はインターフェース + flavor実装を通じて構築されます。メインのsource setで、PaymentServiceインターフェースが宣言されます。src/free/には、AdMobを介して支払い前に広告を表示する実装があります。src/paid/には、支払いゲートウェイに直接進む実装があります。PaymentServiceを使用するコードは、どの実装がロードされているかを知りません。これはコンパイル時に解決されます。このアプローチにより、開発者が誤って呼び出したとしても、サブスクリプション管理コードが無料版に入らないことが保証されます。

異なるflavorのAPKサイズは、依存関係の包含/除外により5〜15MB異なる場合があります。特定のflavorからライブラリを除外するには、build.gradleでflavor固有の依存関係を使用します:freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'。この依存関係はfreeバリアントにのみ追加され、有料版のサイズは増加しません。共有依存関係にはimplementationを使用します。すべてのflavorがそれらを含みます。

kotlin
// src/main/kotlin/com/example/payment/PaymentService.kt
interface PaymentService {
    fun processOrder(amount: Double, callback: (PaymentResult) -> Unit)
}

// src/free/kotlin/.../FreePaymentService.kt
class FreePaymentService : PaymentService {
    override fun processOrder(amount: Double, callback: (PaymentResult) -> Unit) {
        AdManager.showInterstitial {
            PaymentGateway.charge(amount, callback)
        }
    }
}

// src/paid/kotlin/.../PaidPaymentService.kt
class PaidPaymentService : PaymentService {
    override fun processOrder(amount: Double, callback: (PaymentResult) -> Unit) {
        PaymentGateway.charge(amount, callback)
    }
}

マルチモジュールプロジェクトでのProduct Flavor

マルチモジュールプロジェクトでは、ライブラリモジュールに独自のProduct Flavorsがない場合があり、問題が発生します。ライブラリは1回(releaseとして)ビルドされますが、flavorを持つアプリモジュールは対応するバリアントのライブラリを期待します。AGP 8.1以降、ライブラリはpublishing.multipleVariantsブロックを通じて複数のバリアントを公開できます。これにより、ライブラリのすべてのflavorバリアントを単一のmavenリポジトリに公開でき、アプリモジュールが自動的に正しいものを選択します。

別のアプローチは、ライブラリにアプリモジュールと同じflavorDimensionsとproductFlavorsを宣言することです。AGPは1つの次元内で正確な名前の一致によってflavorを自動的に照合します。ライブラリのflavor名がアプリの名前と一致する場合、AGPは一貫したバリアントを作成します。保守を容易にするために、共通のflavor定義をConvention Pluginに抽出することをお勧めします。これはプロジェクトのすべてのモジュールに適用されるGradleプラグインです。

公開を目的としないライブラリ(内部モジュール)の場合、ルートプロジェクトのbuild.gradleを介してflavorを同期するだけで十分です。Gradleはsubprojectsメソッドを提供し、すべてのサブプロジェクトに設定を適用できます。ただし、subprojectsに設定を入れすぎると設定フェーズが遅くなることに注意してください。Convention Pluginsを使用することをお勧めします。これらは1回コンパイルされて再利用され、設定時間を15〜30%削減します。

よくある質問

いくつのProduct Flavorsを作成できますか?

数量に制限はありませんが、各次元がBuild Variantsの数を乗算します。1つの次元に4つのflavor + 2つのbuild types = 8つのバリアント。2つの次元に4 + 4 = 16のバリアント。3次元以下、合計10〜12バリアント以下にすることをお勧めします。

flavorのマニフェストを上書きできますか?

はい、source set src/<flavor>/AndroidManifest.xmlを介して可能です。マニフェストはメインのものとマージされます。ブロック全体を置き換えるには、tools:node="replace"を使用します。たとえば、特定のflavorのアプリラベルや権限を置き換えます。

flavor固有の依存関係を追加するには?

<flavorName>Implementation設定を使用します。例:freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'。この依存関係はfreeバリアントをビルドするときのみ含まれます。有料版の場合:paidImplementation。共通の依存関係はimplementationを介して指定されます。

Product FlavorとBuild Typeの違いは?

Product Flavorはプロダクトバージョン(free、paid、demo)を定義し、Build Typeはビルド方法(debug、release)を定義します。flavorはapplicationId、versionName、リソースを上書きできます。Build Typeはdebuggable、minification、signingを制御します。両方は直交しており、Build Variantに結合されます。

Jetpack ComposeでProduct Flavorを使用できますか?

はい、Product Flavorsは制限なくComposeで動作します。異なるflavorは、source setsまたは抽象クラスの実装を通じて異なるCompose画面を持つことができます。また、flavor固有のCompose依存関係を追加することもできます:freeImplementation 'androidx.compose.ui:ui-tooling'

まとめ

  • Product Flavor — 単一のコードベースからアプリの複数バージョンを作成するためのGradleメカニズム。
  • Flavor Dimensionsはflavorを次元にグループ化し、アプリのさまざまな側面を組み合わせることができます。
  • Source setsはflavorのメインディレクトリを変更せずにリソース、コード、マニフェストを上書きします。
  • インターフェース + flavor実装は機能を分離するためのクリーンなアーキテクチャアプローチです。
  • Flavor固有の依存関係は不要なライブラリが不適切なバージョンに入るのを防ぎます。
  • マルチモジュールプロジェクトでは、Convention Pluginsまたは複数バリアント公開によるflavor同期が必要です。
  • 推奨:プロジェクトでは3つ以下のflavor次元と、合計10以下のBuild Variantsに抑えてください。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください