Product Flavor: что это такое, конфигурация и примеры в Gradle

Автор: IT Sectr Опубликовано: 2026-05-30 Время чтения: 9 мин

Product Flavor в Android-разработке — это механизм Gradle, который позволяет создавать несколько вариантов одного приложения из общей кодовой базы. Каждый flavor может иметь собственный applicationId, ресурсы, зависимости и функциональность — например, бесплатную и платную версии. По данным Google Android Developers, 2025, Product Flavors входят в систему Build Variants и объединяются с Build Types через flavorDimensions. Это стандартный подход для публикации нескольких версий приложения в Google Play.

Главное

  • Product Flavor — вариант продукта с уникальным applicationId, ресурсами и кодом.
  • Flavor Dimensions группируют flavor-ы в независимые оси для многомерной конфигурации.
  • Source sets для flavor-а переопределяют main-ресурсы: иконки, строки, манифест.
  • Gradle автоматически генерирует Build Variant для каждой комбинации flavor + build type.
  • Google Play поддерживает публикацию нескольких flavor-ов как отдельных приложений или одного с разными конфигурациями.

Что такое Product Flavor?

Product Flavor — это конфигурация Gradle в блоке android.productFlavors, которая описывает вариант продукта. Каждый flavor может переопределять applicationId, versionName, versionCode, minSdkVersion, targetSdkVersion, signingConfig и другие параметры defaultConfig. Product Flavors не имеют ограничений по количеству: проект может содержать 2, 5, 10 flavor-ов — Gradle обработает все комбинации.

Product Flavor решает задачу codebase reuse — когда из одного репозитория нужно собрать несколько различающихся приложений. Типичные сценарии: бесплатная версия с рекламой и платная без; демо-версия с ограниченным функционалом; корпоративная и потребительская версии; white-label приложения для разных клиентов. Без Product Flavors каждую версию пришлось бы поддерживать в отдельном проекте, что ведёт к дублированию кода в 60-70%.

Исторически Product Flavors появились в Android Gradle Plugin 0.9 (2013) как замена ant-конфигурациям. До этого разработчики использовали отдельные проекты для разных версий или ручную замену ресурсов перед сборкой. Внедрение flavor-ов в AGP унифицировало подход и сделало его стандартом. По данным опроса JetBrains, 2024, 78% Android-проектов с несколькими версиями используют Product Flavors, остальные — ручное переключение через BuildConfig или reflection.

Product Flavor vs Build Type

Build Type управляет процессом сборки (debug с отладкой, release c оптимизацией). Product Flavor управляет содержимым сборки (free без платных функций, paid с ними). Build Type — это инфраструктурная настройка, Product Flavor — продуктовая. Оба понятия ортогональны: debug сборка free-flavor отличается от release сборки free-flavor только параметрами компиляции, но не функциональностью. Product Flavor нельзя использовать для отключения отладчика — это задача Build Type.

Flavor Dimensions: организация измерений

Порядок измерений и приоритет

Flavor Dimensions (измерения) — это механизм группировки Product Flavors в независимые категории. Если у приложения есть бесплатная/планная версия и отдельно американский/европейский регион, flavour группируются в два измерения: "tier" (free, paid) и "region" (us, eu). Gradle создаёт декартово произведение измерений: freeUs, freeEu, paidUs, paidEu — 4 варианта. Без измерений Gradle воспринимал бы все четыре flavour как одну плоскость, и выбрать можно было бы только один.

Измерения объявляются в блоке flavorDimensions строкой или списком строк. Порядок измерений влияет на приоритет source sets: первое измерение имеет наивысший приоритет. Если измерение A (tier) указано первым, то src/free/ будет переопределять src/us/ в случае конфликта ресурсов. Также порядок влияет на то, как формируется имя Variant: сначала идут flavour первого измерения, затем второго, затем Build Type: freeUsDebug.

Количество измерений не ограничено, но каждое новое измерение умножает количество Build Variants. Для проекта с 4 измерениями (по 2 flavour) и 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

Создание Product Flavors в build.gradle

Kotlin DSL для Product Flavors

Для создания Product Flavor необходимо добавить блок productFlavors внутри android, указать имя flavor и его параметры. Минимальное объявление flavor — это имя и dimension. Все остальные параметры наследуются из defaultConfig и могут быть переопределены. Flavor наследует defaultConfig полностью, включая applicationId, versionCode, testInstrumentationRunner.

Каждый flavor может переопределять applicationId — это позволяет устанавливать несколько версий приложения на одно устройство одновременно. Например, free-версия будет com.example.app.free, paid — com.example.app.paid. Если applicationId не переопределён, все flavour будут иметь одинаковый идентификатор, и устанавливать их параллельно не получится. applicationId должен совпадать с package в манифесте (если не используется applicationIdSuffix).

AGP 8+ рекомендует использовать Kotlin DSL вместо Groovy для build.gradle. Kotlin DSL даёт type-safe доступ к конфигурации: IDE подсказывает имена параметров, проверяет типы на этапе компиляции и подсвечивает ошибки. Миграция с Groovy на Kotlin DSL для Product Flavors обычно состоит из замены кавычек на скобки и добавления типов. 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 set — директорию src/<flavorName>/. В этой директории можно размещать переопределённые ресурсы, исходники и манифест. Source set flavor работает как оверлей поверх main: файлы из src/free/res/ переопределяют файлы из src/main/res/ с теми же именами. Это позволяет иметь разные строки, иконки, цвета и макеты для каждого flavor без изменения основного кода.

Для переопределения Java/Kotlin-классов существует два подхода: flavor-specific implementation (реализация абстрактного класса в каждом flavor) и BuildConfig field (ветвление в коде). Первый подход чище: вы определяете интерфейс или абстрактный класс в main, а конкретные реализации — в src/free/ и src/paid/. При сборке компилируется только реализация текущего flavor. Это даёт одновременные преимущества: меньший размер APK (неплатный код не попадает в free-версию) и безопасность (невозможно по ошибке вызвать платную функцию).

AndroidManifest.xml в source set flavor не заменяет, а сливается с main-манифестом. Слияние происходит по правилам Android: одинаковые атрибуты в одном элементе переопределяются, уникальные — добавляются. Например, если в main манифесте объявлен INTERNET permission, а в free — нет, интернет останется. Но tools:node="replace" позволяет заменить целый блок манифеста для конкретного flavor. Это полезно, когда разные flavour требуют разных permissions (запись на SD-карту для paid, камера для free).

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 — без рекламы, с расширенным функционалом. Для free-версии устанавливается applicationId "com.example.app.free", для paid — "com.example.app.paid". Обе версии могут быть установлены на одно устройство одновременно, так как applicationId является уникальным идентификатором приложения в системе Android.

Архитектурно разделение строится через interface + flavor implementation. В main source set объявляется интерфейс PaymentService. В src/free/ лежит реализация, которая показывает рекламу перед оплатой через AdMob. В src/paid/ — реализация, которая сразу переходит к платёжному шлюзу. Код, использующий PaymentService, не знает, какая реализация загружена — это решается на этапе компиляции. Такой подход гарантирует, что в free-версию не попадёт код управления подписками, даже если разработчик случайно его вызовет.

Размер APK для разных flavor может отличаться на 5-15 МБ из-за включения/исключения зависимостей. Чтобы исключить библиотеку из конкретного flavor, используется flavor-specific dependencies в build.gradle: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Эта зависимость будет добавлена только для free-варианта и не увеличит размер paid-версии. Для общих зависимостей используется 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, что создаёт проблему: библиотека собирается один раз (как release), а app-модуль с flavor ожидает библиотеку с соответствующим вариантом. Начиная с AGP 8.1, библиотеки могут публиковать multiple variants через блок publishing.multipleVariants — это позволяет опубликовать все flavor-варианты библиотеки в один maven-репозиторий, и app-модуль автоматически выберет нужный.

Альтернативный подход — объявить те же flavorDimensions и productFlavors в библиотеке, что и в app-модуле. AGP автоматически сопоставляет flavour по полному совпадению имени из одного измерения. Если имя flavor в библиотеке совпадает с именем в app, AGP создаст согласованные варианты. Для удобства поддержки рекомендуется выносить общие определения flavour в Convention Plugin — Gradle-плагин, который применяется ко всем модулям проекта.

Для библиотек, не предназначенных для публикации (внутренние модули), достаточно синхронизировать flavour через build.gradle корневого проекта. Gradle предоставляет метод subprojects, который позволяет применить конфигурацию ко всем подпроектам. Однако стоит помнить, что слишком большая конфигурация в subprojects замедляет configuration phase. Рекомендуется использовать Convention Plugins — они компилируются один раз и переиспользуются, что сокращает время конфигурации на 15-30%.

Часто задаваемые вопросы

Сколько Product Flavors можно создать?

Ограничений по количеству нет, но каждое измерение умножает число Build Variants. 4 flavour в одном измерении + 2 build types = 8 вариантов. 4 + 4 в двух измерениях = 16 вариантов. Рекомендуется не более 3 измерений и 10-12 итоговых вариантов.

Можно ли переопределить манифест для flavour?

Да, через source set src/<flavor>/AndroidManifest.xml. Манифест сливается с основным. Для замены целого блока используйте tools:node="replace". Например, заменить label приложения или разрешения для конкретного flavor.

Как добавить flavour-specific зависимости?

Используйте конфигурацию <flavorName>Implementation. Пример: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Такая зависимость будет включена только при сборке free-варианта. Для paid: paidImplementation. Общие зависимости указываются через implementation.

Чем Product Flavor отличается от Build Type?

Product Flavor определяет версию продукта (free, paid, demo), Build Type — способ сборки (debug, release). Flavour могут переопределять applicationId, versionName, ресурсы. Build Type управляет debuggable, minification, signing. Оба ортогональны и комбинируются в Build Variant.

Можно ли использовать Product Flavor с Jetpack Compose?

Да, Product Flavors работают с Compose без ограничений. Разные flavor могут иметь разные Compose-экраны через source sets или реализацию абстрактных классов. Также можно добавлять flavor-specific Compose-зависимости: freeImplementation 'androidx.compose.ui:ui-tooling'.

Итоги

  • Product Flavor — механизм Gradle для создания нескольких версий приложения из одного кода.
  • Flavor Dimensions группируют flavor в измерения, позволяя комбинировать разные аспекты приложения.
  • Source sets для flavor переопределяют ресурсы, код и манифест без изменения main-директории.
  • Interface + flavor implementation — чистый архитектурный подход для разделения функциональности.
  • Flavor-specific dependencies предотвращают попадание лишних библиотек в неподходящие версии.
  • Многомодульные проекты требуют синхронизации flavour через Convention Plugins или multiple variants publishing.
  • Рекомендация: не более 3 измерений flavour и не более 10 итоговых Build Variants в проекте.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также