Build Variant в Android-розробці — це комбінація build type і product flavor, яка визначає, як буде зібрано APK або AAB: з якими параметрами, ресурсами і кодом. Кожен варіант збірки представляє окрему конфігурацію Gradle зі своїм applicationId, ключами підпису та включеними залежностями. За даними Google Android Developers, 2025, правильне налаштування Build Variants скорочує час збірки до 40% за рахунок виключення непотрібних ресурсів для кожного варіанту. Система варіантів збірки — основа конфігураційного управління в сучасних Android-проектах.
Головне
Build Variant — це результат комбінації одного Build Type і одного Product Flavor. Якщо в проекті не визначені Product Flavor, то Build Variant збігається з Build Type. Gradle автоматично генерує повний набір варіантів як декартів добуток усіх FlavorDimensions, Product Flavors і Build Types. Наприклад, для flavor free/paid і типів debug/release буде створено 4 варіанти: freeDebug, freeRelease, paidDebug, paidRelease.
Кожен Build Variant отримує власне ім'я у форматі <Flavor><Type> з великої літери flavor. Для цього варіанту Gradle генерує окремі завдання: assembleFreeDebug, installFreeDebug, bundleFreeRelease. В Android Studio перемикання між варіантами доступне через панель Build Variants (View → Tool Windows → Build Variants). Вибір варіанту впливає на те, який код компілюється, які ресурси включаються і який APK/AAB створюється.
Система Build Variants вирішує три ключові завдання: розділення конфігурацій для різних середовищ (dev/staging/production), створення кількох версій застосунку (free/paid) і A/B-тестування збірок. Без Build Variants розробникам довелося б вручну перемикати прапорці та конфігурації, що веде до помилок людського фактору. За даними дослідження Gradle Inc., 2024, впровадження Build Variants знижує кількість помилок збірки на 60% в проектах із трьома і більше середовищами розгортання.
AGP (Android Gradle Plugin) обчислює всі комбінації на етапі конфігурації. Якщо в проекті два виміри з двома і трьома flavor відповідно, Gradle створить 2 × 2 × 3 = 12 комбінацій, помножених на кількість Build Types (зазвичай 2). Кожна комбінація отримує унікальне ім'я та набір завдань. AGP автоматично додає source set для кожного варіанту: src/freeDebug/, src/paidRelease/, а також узагальнені src/free/ і src/debug/. Пріоритет читання ресурсів: variant → flavor → type → main.
// Приклад: 4 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// Разом: 2 × 2 × 2 = 8 варіантів
android {
flavorDimensions "version", "server"
productFlavors {
demo { dimension "version" }
prod { dimension "version" }
mock { dimension "server" }
live { dimension "server" }
}
}
Build Type визначає як збирати застосунок — з налагоджувальною інформацією чи без, з оптимізацією чи без, з яким підписом. Product Flavor визначає що збирати — яку версію продукту. Build Type — це механізм збірки (debug, release, staging). Product Flavor — це варіант продукту (free, paid, enterprise, demo). Обидва поняття ортогональні: будь-який Build Type можна застосувати до будь-якого Product Flavor.
Build Type за замовчуванням включає debug (debuggable=true, minification=false, signing=debug.keystore) і release (debuggable=false, minification=true, signing=production.keystore). Product Flavor за замовчуванням — один, без назви (фактично main source set). Розробник може додавати свої Build Types (наприклад, «staging» з debuggable=true і minification=true) і Product Flavors у будь-якій кількості. Різниця також у тому, що Build Type не можна групувати у виміри, а Product Flavor — можна.
Ключова практична відмінність: defaultConfig в build.gradle застосовується до всіх Variants, але може бути перевизначений в productFlavors і buildTypes. BuildConfigField, доданий в buildType, видимий у всіх flavor цього типу, а доданий в productFlavor — у всіх типах цього flavor. Якщо поле визначено і там, і там — пріоритет у buildType (він застосовується останнім у ланцюжку).
| Характеристика | Build Type | Product Flavor |
|---|---|---|
| Призначення | Як збирати | Що збирати |
| Приклади | debug, release, staging | free, paid, demo, enterprise |
| За замовчуванням | debug + release | один (main) |
| Source Set | src/debug/, src/release/ | src/free/, src/paid/ |
| Виміри | ні | flavorDimensions |
| Застосування | після flavor, перевизначає | після defaultConfig |
| BuildConfigField | перевизначає flavor | перевизначає defaultConfig |
Налаштування Build Variants виконується в блоці android файлу build.gradle рівня модуля. Спочатку оголошуються buildTypes з їхніми параметрами, потім flavorDimensions і productFlavors. Gradle автоматично створює варіанти на основі цих оголошень. Кожен варіант успадковує defaultConfig модуля, перевизначаючи вказані поля. Порядок оголошення впливає на пріоритет: buildTypes застосовуються після productFlavors.
Для доступу до конкретного Build Variant в Gradle-скриптах використовується android.applicationVariants (для app модуля) або android.libraryVariants (для бібліотечного модуля). Це колекція, по якій можна ітеруватися та змінювати конфігурацію кожного варіанту в рантаймі конфігурації. Наприклад, можна програмно додавати buildConfigField для всіх варіантів, що містять слово «demo».
Android Gradle Plugin 8.x додав підтримку onVariants — більш чисте API для налаштування варіантів через лямбди. Старе API (variantOutput, variantFilter) позначено як deprecated. Рекомендується використовувати onVariants разом з onEach для бібліотечних модулів. Міграція з variantOutput на onVariants — рекомендований крок при оновленні AGP з 7.x на 8.x.
android {
buildTypes {
debug {
debuggable true
minification false
signingConfig signingConfigs.debug
}
release {
debuggable false
minification true
proguardFiles "proguard-rules.pro"
signingConfig signingConfigs.release
}
staging {
debuggable true
minification true
versionNameSuffix "-staging"
}
}
flavorDimensions "tier", "region"
productFlavors {
free { dimension "tier" }
paid { dimension "tier" }
us { dimension "region" }
eu { dimension "region" }
}
}
android.onVariants { variant ->
if (variant.name.contains("Demo")) {
variant.setEnabled(false)
}
}
Кожен Build Variant отримує власну ієрархію source sets — директорій з вихідним кодом, ресурсами та маніфестом. Source set локалізується в src/<variantName>/ (наприклад, src/freeDebug/) і може містити java/, res/, AndroidManifest.xml, assets/. Якщо файл існує в source set варіанту, він перевизначає файл з тим же ім'ям з основного source set (src/main/). Для ресурсів працює злиття, а не заміна — система об'єднує ресурси з усіх активних source sets, віддаючи пріоритет variant-специфічним.
Source sets для Build Variant будуються по ланцюжку: src/main/ → src/flavor/ → src/type/ → src/flavorType/. Наприклад, для paidRelease спочатку застосовується main, потім paid, потім release, потім paidRelease. Кожен наступний source set перевизначає попередній. Це означає, що src/release/res/values/strings.xml перевизначить ті ж рядки з src/paid/, але src/paidRelease/res/ — ще більш пріоритетний.
Використання source sets для варіантів — рекомендований спосіб кастомізації ресурсів. Замість того щоб перевіряти BuildConfig.FLAVOR в коді і гілкувати логіку, можна просто помістити різні файли в різні source sets. Наприклад, іконки для free і paid версій кладуться в src/free/res/ і src/paid/res/ відповідно, а AndroidManifest з різними дозволами — в src/free/AndroidManifest.xml і src/paid/AndroidManifest.xml. Це чистіше, швидше (ресурси компілюються, а не перевіряються в runtime) і безпечніше (не можна випадково включити платну функціональність у безкоштовній версії через баг у коді).
У багатомодульних проектах кожен модуль (бібліотека) може мати власні Build Variants. AGP автоматично синхронізує варіанти: якщо app-модуль збирає paidRelease, всі залежні бібліотеки також збираються в їхніх варіантах, що відповідають paidRelease. Проблема виникає, коли бібліотека не має product flavors, але app-модуль має — тоді бібліотека збирається один раз (release або debug залежно від типу).
Для бібліотечних модулів Build Variant за замовчуванням збігається з Build Type app-модуля, оскільки в бібліотек немає product flavors. Якщо бібліотека повинна підлаштовуватися під flavor app-модуля, необхідно оголосити ті ж flavorDimensions і productFlavors в бібліотеці. AGP зіставляє flavor за повним збігом імені. Gradle рекомендує синхронізувати flavors через build-конфігурацію в кореневому проекті за допомогою subprojects або Convention Plugins.
Починаючи з AGP 8.1, бібліотеки можуть публікувати multiple variants — опублікувати в maven-репозиторій всі варіанти бібліотеки одночасно. Це вирішує проблему, коли app-модуль використовує paid flavor, а бібліотека опублікована тільки для free. Multiple variants publishing (MVP) дозволяє залежному проекту вибрати потрібний варіант автоматично. Для включення MVP потрібно додати publishing { multipleVariants { ... } } в build.gradle бібліотеки.
Іноді необхідно вимкнути частину Build Variants — наприклад, якщо комбінація mockRelease не має сенсу (мок-сервер не повинен потрапляти в продакшн). Gradle надає variantFilter — DSL-блок, в якому можна перевіряти властивості кожного варіанту і вимикати його через setIgnore(true). VariantFilter застосовується на етапі конфігурації, до створення завдань, тому вимкнений варіант не генерує assemble та install завдання.
Фільтрація також корисна для прискорення збірки. Якщо в проекті 8 варіантів, а розробник працює тільки над одним, решта 7 варіантів все одно проходять конфігурацію. При використанні variantFilter вимкнені варіанти не створюють завдань, що скорочує час конфігурації на 30-50% для проектів з 6+ flavor-вимірами. В CI/CD можна динамічно фільтрувати варіанти через параметри командного рядка -PbuildOnly=paidRelease.
android {
variantFilter { variant ->
// Вимикаємо mock для release і demo для production
def names = variant.flavors*.name
def isMock = names.contains("mock")
def isDemo = names.contains("demo")
def isRelease = variant.buildType.name == "release"
if ((isMock && isRelease) || (isDemo && !isMock)) {
variant.setIgnore(true)
}
}
}
// Динамічна фільтрація через параметри
if (project.hasProperty("buildOnly")) {
def target = project.property("buildOnly")
android.variantFilter { variant ->
variant.setIgnore(variant.name != target)
}
}
Часті запитання
Обмежень немає, але Gradle створює декартів добуток всіх flavor і типів. Якщо у вас 3 виміри по 3 flavor і 3 build type — вийде 27 варіантів. Занадто багато варіантів сповільнюють конфігурацію. Рекомендується не більше 10-12 варіантів в одному модулі.
flavorDimensions групують Product Flavors у незалежні осі. Наприклад, вимір «tier» (free, paid) і «region» (us, eu). Без вимірів всі flavor належать до однієї осі, і Gradle буде вибирати тільки один flavor з усіх (не можна мати free+us і paid+eu як окремі варіанти).
У блоці productFlavor або buildType вкажіть applicationId. Наприклад, для free-версії: free { applicationId «com.example.app.free» }. У маніфесті використовуйте ${applicationId} — Gradle автоматично підставить значення. Це дозволяє встановити обидва варіанти на один пристрій.
В iOS аналогом Build Variants є комбінація Scheme + Configuration. Xcode Schemes налаштовуються через конфігурації Debug/Release з різними параметрами. Для кількох версій (free/paid) використовуються Build Configurations і Preprocessor Macros. На Android концепція формалізована суворіше і вбудована в Gradle.
Так, кожен варіант може мати різний розмір APK. debug-збірки включають налагоджувальну інформацію, SDK і непідтримувані ресурси. Release-збірки з minification і resource shrinking дають мінімальний розмір. Product Flavor також впливає: free-версія без платних бібліотек буде меншою за paid-версію на розмір цих бібліотек.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також