Build Variant în dezvoltarea Android este o combinație între build type și product flavor care determină cum va fi construit APK-ul sau AAB-ul: cu ce parametri, resurse și cod. Fiecare variantă de compilare reprezintă o configurație separată Gradle cu propriul applicationId, chei de semnare și dependențe incluse. Conform Google Android Developers, 2025, configurarea corectă a Build Variants reduce timpul de compilare cu 40% prin excluderea resurselor inutile pentru fiecare variantă. Sistemul de variante de compilare stă la baza managementului configurației în proiectele Android moderne.
Principalele puncte
Build Variant — este rezultatul combinării unui Build Type și a unui Product Flavor. Dacă în proiect nu sunt definite Product Flavor, Build Variant coincide cu Build Type. Gradle generează automat setul complet de variante ca produs cartezian al tuturor FlavorDimensions, Product Flavors și Build Types. De exemplu, pentru flavor free/paid și tipurile debug/release se vor crea 8 variante: freeDebug, freeRelease, paidDebug, paidRelease.
Fiecare Build Variant primește propriul nume în formatul <Flavor><Type> cu flavor cu literă mare. Pentru această variantă, Gradle generează sarcini separate: assembleFreeDebug, installFreeDebug, bundleFreeRelease. În Android Studio, comutarea între variante este disponibilă prin panoul Build Variants (View → Tool Windows → Build Variants). Selectarea variantei influențează ce cod este compilat, ce resurse sunt incluse și ce APK/AAB este creat.
Sistemul Build Variants rezolvă trei sarcini cheie: separarea configurațiilor pentru diferite medii (dev/staging/production), crearea mai multor versiuni ale aplicației (free/paid) și testarea A/B a compilărilor. Fără Build Variants, dezvoltatorii ar trebui să comute manual flagurile și configurațiile, ceea ce duce la erori de factor uman. Conform cercetării Gradle Inc., 2024, implementarea Build Variants reduce numărul erorilor de compilare cu 60% în proiecte cu trei sau mai multe medii de implementare.
AGP (Android Gradle Plugin) calculează toate combinațiile în faza de configurare. Dacă proiectul are două dimensiuni cu două și, respectiv, trei flavor, Gradle va crea 2 × 2 × 3 = 12 combinații, înmulțite cu numărul de Build Types (de obicei 2). Fiecare combinație primește un nume unic și un set de sarcini. AGP adaugă automat un source set pentru fiecare variantă: src/freeDebug/, src/paidRelease/, precum și cele generalizate src/free/ și src/debug/. Prioritatea citirii resurselor: variant → flavor → type → main.
// Exemplu: 4 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// Total: 2 × 2 × 2 = 8 variante
android {
flavorDimensions "version", "server"
productFlavors {
demo { dimension "version" }
prod { dimension "version" }
mock { dimension "server" }
live { dimension "server" }
}
}
Build Type determină cum se compilează aplicația — cu informații de depanare sau fără, cu optimizare sau fără, cu ce semnare. Product Flavor determină ce se compilează — ce versiune a produsului. Build Type — este mecanismul de compilare (debug, release, staging). Product Flavor — este varianta produsului (free, paid, enterprise, demo). Ambele concepte sunt ortogonale: orice Build Type poate fi aplicat oricărui Product Flavor.
Build Type implicit include debug (debuggable=true, minification=false, signing=debug.keystore) și release (debuggable=false, minification=true, signing=production.keystore). Product Flavor implicit este unul singur, fără nume (de fapt main source set). Dezvoltatorul poate adăuga propriile Build Types (de exemplu, „staging” cu debuggable=true și minification=true) și Product Flavors în orice număr. Diferența constă și în faptul că Build Type nu poate fi grupat în dimensiuni, iar Product Flavor — poate.
Diferența practică cheie: defaultConfig în build.gradle se aplică tuturor Variants, dar poate fi suprascris în productFlavors și buildTypes. BuildConfigField adăugat în buildType este vizibil în toate flavorurile acestui tip, iar cel adăugat în productFlavor — în toate tipurile acestui flavor. Dacă câmpul este definit și acolo, și acolo — prioritate are buildType (se aplică ultimul în lanț).
| Caracteristică | Build Type | Product Flavor |
|---|---|---|
| Scop | Cum să compilezi | Ce să compilezi |
| Exemple | debug, release, staging | free, paid, demo, enterprise |
| Implicit | debug + release | unul (main) |
| Source Set | src/debug/, src/release/ | src/free/, src/paid/ |
| Dimensiuni | nu | flavorDimensions |
| Aplicare | după flavor, suprascrie | după defaultConfig |
| BuildConfigField | suprascrie flavor | suprascrie defaultConfig |
Configurarea Build Variants se realizează în blocul android al fișierului build.gradle la nivel de modul. Mai întâi se declară buildTypes cu parametrii lor, apoi flavorDimensions și productFlavors. Gradle creează automat variante pe baza acestor declarații. Fiecare variantă moștenește defaultConfig al modulului, suprascriind câmpurile specificate. Ordinea declarației influențează prioritatea: buildTypes se aplică după productFlavors.
Pentru accesul la un Build Variant specific în scripturile Gradle se folosește android.applicationVariants (pentru modulul app) sau android.libraryVariants (pentru modulul de bibliotecă). Aceasta este o colecție peste care se poate itera și modifica configurația fiecărei variante în timpul configurației. De exemplu, se poate adăuga programatic buildConfigField pentru toate variantele care conțin cuvântul „demo”.
Android Gradle Plugin 8.x a adăugat suport pentru onVariants — o API mai curată pentru configurarea variantelor prin lambda. API veche (variantOutput, variantFilter) este marcată ca deprecată. Se recomandă utilizarea onVariants împreună cu onEach pentru modulele bibliotecă. Migrarea de la variantOutput la onVariants — pas recomandat la actualizarea AGP de la 7.x la 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)
}
}
Fiecare Build Variant primește propria ierarhie de source sets — directoare cu cod sursă, resurse și manifest. Source set se localizează în src/<variantName>/ (de exemplu, src/freeDebug/) și poate conține java/, res/, AndroidManifest.xml, assets/. Dacă un fișier există în source set-ul variantei, acesta suprascrie fișierul cu același nume din source set-ul principal (src/main/). Pentru resurse funcționează îmbinarea, nu înlocuirea — sistemul combină resursele din toate source sets active, acordând prioritate celor specifice variantei.
Source sets pentru Build Variant se construiesc în lanț: src/main/ → src/flavor/ → src/type/ → src/flavorType/. De exemplu, pentru paidRelease se aplică mai întâi main, apoi paid, apoi release, apoi paidRelease. Fiecare source set următor îl suprascrie pe precedentul. Aceasta înseamnă că src/release/res/values/strings.xml va suprascrie aceleași șiruri din src/paid/, dar src/paid/release/res/ este și mai prioritar.
Utilizarea source sets pentru variante — modul recomandat de personalizare a resurselor. În loc să verifici BuildConfig.FLAVOR în cod și să ramifici logica, poți pur și simplu plasa diferite fișiere în diferite source sets. De exemplu, pictogramele pentru versiunile free și paid se pun în src/free/res/ și respectiv src/paid/res/, iar AndroidManifest cu permisiuni diferite — în src/free/AndroidManifest.xml și src/paid/AndroidManifest.xml. Este mai curat, mai rapid (resursele se compilează, nu se verifică în runtime) și mai sigur (nu se poate activa accidental funcționalitatea plătită în versiunea gratuită din cauza unei erori în cod).
În proiectele multi-modul, fiecare modul (bibliotecă) poate avea propriile Build Variants. AGP sincronizează automat variantele: dacă modulul app compilează paidRelease, toate bibliotecile dependente sunt și ele compilate în variantele corespunzătoare paidRelease. Problema apare când biblioteca nu are product flavors, dar modulul app are — atunci biblioteca este compilată o singură dată (release sau debug în funcție de tip).
Pentru modulele bibliotecă, Build Variant implicit coincide cu Build Type al modulului app, deoarece bibliotecile nu au product flavors. Dacă biblioteca trebuie să se adapteze la flavorul modulului app, este necesar să se declare aceleași flavorDimensions și productFlavors în bibliotecă. AGP potrivește flavorul după coincidența completă a numelui. Gradle recomandă sincronizarea flavorurilor prin configurarea de compilare în proiectul rădăcină cu ajutorul subprojects sau Convention Plugins.
Începând cu AGP 8.1, bibliotecile pot publica multiple variants — publică toate variantele bibliotecii simultan în depozitul maven. Aceasta rezolvă problema când modulul app folosește flavorul paid, dar biblioteca este publicată doar pentru free. Multiple variants publishing (MVP) permite proiectului dependent să selecteze automat varianta necesară. Pentru activarea MVP, trebuie adăugat publishing { multipleVariants { ... } } în build.gradle al bibliotecii.
Uneori este necesară dezactivarea unei părți din Build Variants — de exemplu, dacă combinația mockRelease nu are sens (serverul mock nu trebuie să ajungă în producție). Gradle oferă variantFilter — un bloc DSL în care se pot verifica proprietățile fiecărei variante și se poate dezactiva prin setIgnore(true). VariantFilter se aplică în faza de configurare, înainte de crearea sarcinilor, astfel încât varianta dezactivată nu generează sarcini assemble și install.
Filtrarea este utilă și pentru accelerarea compilării. Dacă proiectul are 8 variante, iar dezvoltatorul lucrează doar la una, celelalte 7 variante trec oricum prin configurare (configuration phase). Folosind variantFilter, variantele dezactivate nu creează sarcini, ceea ce reduce timpul de configurare cu 30-50% pentru proiecte cu 6+ dimensiuni de flavor. În CI/CD se pot filtra dinamic variantele prin parametri din linia de comandă -PbuildOnly=paidRelease.
android {
variantFilter { variant ->
// Dezactivăm mock pentru release și demo pentru producție
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)
}
}
}
// Filtrare dinamică prin parametri
if (project.hasProperty("buildOnly")) {
def target = project.property("buildOnly")
android.variantFilter { variant ->
variant.setIgnore(variant.name != target)
}
}
Întrebări frecvente
Nu există limitări, dar Gradle creează produsul cartezian al tuturor flavorurilor și tipurilor. Dacă ai 3 dimensiuni a câte 3 flavor și 3 build type — vei obține 27 de variante. Prea multe variante încetinesc configurarea. Se recomandă nu mai mult de 10-12 variante într-un singur modul.
flavorDimensions grupează Product Flavors în axe independente. De exemplu, dimensiunea „tier” (free, paid) și „region” (us, eu). Fără dimensiuni, toate flavorurile aparțin unei singure axe și Gradle va selecta doar un flavor din toate (nu se pot avea free+us și paid+eu ca variante separate).
În blocul productFlavor sau buildType se specifică applicationId. De exemplu, pentru versiunea free: free { applicationId "com.example.app.free" }. În manifest se folosește ${applicationId} — Gradle va înlocui automat valoarea. Aceasta permite instalarea ambelor variante pe același dispozitiv.
În iOS, analogul Build Variants este combinația Scheme + Configuration. Xcode Schemes se configurează prin configurațiile Debug/Release cu diferiți parametri. Pentru mai multe versiuni (free/paid) se folosesc Build Configurations și Preprocessor Macros. Pe Android, conceptul este formalizat mai riguros și încorporat în Gradle.
Da, fiecare variantă poate avea o dimensiune diferită a APK-ului. Compilările debug includ informații de depanare, SDK și resurse nesuportate. Compilările release cu minificare și reducere a resurselor oferă dimensiunea minimă. Product Flavor influențează și el: versiunea free fără biblioteci plătite va fi mai mică decât versiunea paid cu dimensiunea acestor biblioteci.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și