Android proqramlaşdırmasında Product Flavor — ortaq kod bazasından bir tətbiqin bir neçe variantını yaratmağa imkan veren Gradle mexanizmidir. Hər bir flavor öz applicationId, resurs, asılılıq və funksionallığa malik ola bilər — məsələn, pulsuz və pullu versiyalar. Google Android Developers, 2025-in məlumatına görə, Product Flavors Build Variants sisteminə daxildir və flavorDimensions vasitəsilə Build Types ilə birləşir. Bu, Google Play-də tətbiqin bir neçe versiyasını nəşr etmək üçün standart yanaşmadır.
Başlıca
Product Flavor — android.productFlavors blokunda məhsul variantını təsvir eden Gradle konfiqurasiyasıdır. Hər bir flavor applicationId, versionName, versionCode, minSdkVersion, targetSdkVersion, signingConfig və digər defaultConfig parametrlərini əvəz edə bilər. Product Flavors say məhdudiyyətinə malik deyil: layihədə 2, 5 və ya 10 flavor ola bilər — Gradle bütün kombinasiyaları emal edəcək.
Product Flavor codebase reuse problemini həll edir — bir repozitoridan bir neçe fərqli tətbiq qurmaq lazım olduqda. Tipik ssenarilər: reklamlı pulsuz versiya və reklamsız pullu; məhdud funksiyalı demo versiya; korporativ və istehlakçı versiyaları; fərqli müştərilər üçün white-label tətbiqləri. Product Flavors olmadan hər bir versiyanı ayrı layihədə saxlamaq lazım gələrdi, bu da kodun 60-70% təkrarlanmasına səbəb olur.
Tarixən Product Flavors Android Gradle Plugin 0.9-da (2013) ant-konfiqurasiyaların əvəzi kimi ortaya çıxdı. Bundan əvvəl proqramçılar fərqli versiyalar üçün ayrı layihələrdən və ya qurmadan əvvəl resursları əl ilə dəyişdirməkdən istifadə edirdilər. AGP-də flavor-ların tətbiqi yanaşmanı birləşdirdi və onu standarta çevirdi. JetBrains, 2024 sorğusuna görə, bir neçe versiyalı Android layihələrinin 78% Product Flavors-dan istifadə edir, qalanları — BuildConfig və ya reflection vasitəsilə əl ilə keçiddən.
Build Type qurma prosesini idarə edir (debug və release). Product Flavor qurmanın məzmununu idarə edir (pulsuz ödənişsiz funksiyalar, pullu onlarla). Build Type infrastruktur qurğusudur, Product Flavor isə məhsul qurğusu. Hər iki anlayış ortogonaldır: free-flavor-un debug versiyası free-flavor-un release versiyasından yalnız kompilyasiya parametrləri ilə fərqlənir, funksionallıqla deyil. Product Flavor debuggeri söndürmək üçün istifadə edilə bilməz — bu Build Type-in işidir.
Flavor Dimensions (ölçülər) — Product Flavors-u müstəqil kateqoriyalara qruplaşdırmaq mexanizmidir. Tətbiqin pulsuz/pullu versiyası və ayrıca Amerika/Avropa regionu varsa, flavour iki ölçüyə qruplaşdırılır: „tier” (free, paid) və „region” (us, eu). Gradle ölçülərin dekart hasilini yaradır: freeUs, freeEu, paidUs, paidEu — 4 variant. Ölçülər olmadan Gradle bütün dörd flavour-u bir müstəvi kimi qəbul edərdi və yalnız biri seçilə bilərdi.
Ölçülər flavorDimensions blokunda sətr və ya sətrlər siyahısı kimi elan edilir. Ölçülərin ardıcıllığı source sets prioritetinə təsir edir: birinci ölçü ən yüksək prioritetə malikdir. Ölçü A (tier) birincidirsə, resurs münaqişəsi halında src/free/ src/us/-u əvəz edəcək. Ardıcıllıq həmçinin Variant adına təsir edir: əvvəlcə birinci ölçünün flavour-u, sonra ikincinin, ardından Build Type: freeUsDebug.
Ölçülərin sayı məhdud deyil, lakin hər yeni ölçü Build Variants sayını artırır. 4 ölçülü (hər birində 2 flavour) və 2 build types olan layihə üçün 2 × 2 × 2 × 2 × 2 = 32 variant alınır. Praktik hədd — 3 ölçü (maksimum 8-12 variant). Daha çox — Gradle konfiqurasiyası yavaşlayır və Android Studio-da Build Variants paneli oxunmaz olur.
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
}
}
}
// Nəticə: freeMinApi21, freeMinApi26, paidMinApi21, paidMinApi26
// Hər biri × debug/release = 8 Build Variants
Product Flavor yaratmaq üçün android daxilində productFlavors bloku əlavə edilməli, flavor adı və parametrləri göstərilməlidir. Flavor-un minimum elanı ad və dimension-dur. Qalan bütün parametrlər defaultConfig-dən miras alınır və əvəz edilə bilər. Flavor defaultConfig-i tamamilə miras alır, o cümlədən applicationId, versionCode və testInstrumentationRunner.
Hər bir flavor applicationId-ni əvəz edə bilər — bu, bir cihazda eyni anda bir neçe tətbiq versiyasını qurmağa imkan verir. Məsələn, free versiyası com.example.app.free, paid isə com.example.app.paid olacaq. applicationId əvəz edilməzsə, bütün flavour eyni identifikatora malik olacaq və onları paralel qurmaq mümkün olmayacaq. applicationId manifestdəki package ilə uyğun olmalıdır (applicationIdSuffix istifadə edilməyibsə).
AGP 8+ build.gradle üçün Groovy əvəzinə Kotlin DSL istifadəsini tövsiyə edir. Kotlin DSL konfiqurasiyaya tip-təhlükəsiz çıxış təmin edir: IDE parametr adlarını təklif edir, kompilyasiya mərhələsində tipləri yoxlayır və səhvləri vurğulayır. Groovy-dən Kotlin DSL-ə miqrasiya adətən dırnaqların mötərizələrlə əvəz edilməsi və tiplərin əlavə edilməsindən ibarətdir. AGP geriyə uyğundur — hər iki sintaksis bir layihədə paralel işləyir.
// 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")
}
}
}
Hər Product Flavor öz source set-ini yaradır — src/<flavorName>/ qovluğu. Bu qovluqda əvəz edilmiş resurslar, mənbələr və manifest yerləşdirilə bilər. Flavor source set main üzərində örtük kimi işləyir: src/free/res/-dəki fayllar eyni adlı src/main/res/ fayllarını əvəz edir. Bu, əsas kodu dəyişdirmədən hər flavor üçün fərqli sətrlər, nişanlar, rənglər və maketlərə malik olmağa imkan verir.
Java/Kotlin siniflərini əvəz etmək üçün iki yanaşma var: flavor-specific implementation (hər flavor-da abstrakt sinifin tətbiqi) və BuildConfig field (kodda budaqlanma). Birinci yanaşma daha təmizdir: main-də interfeys və ya abstrakt sinif təyin edirsiniz, konkret tətbiqlər isə src/free/ və src/paid/-də yerləşir. Qurma zamanı yalnız cari flavor-un tətbiqi kompilyasiya olunur. Bu, eyni anda həm APK ölçüsünü azaldır (ödənişsiz kod free versiyasına daxil olmur) və təhlükəsizliyi təmin edir (ödənişli funksiyanı təsadüfən çağırmaq mümkün deyil).
Source set flavor-dakı AndroidManifest.xml əvəz etmir, əksinə main manifesti ilə birləşir. Birləşmə Android qaydalarına uyğun baş verir: eyni elementdəki eyni atributlar əvəz edilir, unikal olanlar əlavə edilir. Məsələn, main manifestdə INTERNET icazəsi elan edilibsə, free-də isə yox, internet qalacaq. Lakin tools:node="replace" konkret flavor üçün bütün manifest blokunu əvəz etməyə imkan verir. Bu, fərqli flavour fərqli icazələr tələb etdikdə faydalıdır (paid üçün SD-yə yazma, free üçün kamera).
<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>
Tipik ssenarini nəzərdən keçirək: free — reklamlı və əsas funksiyalı versiya, paid — reklamsız, genişləndirilmiş funksionallıqlı. Free versiyası üçün applicationId „com.example.app.free”, paid üçün isə „com.example.app.paid” qurulur. Hər iki versiya eyni cihazda eyni anda qurula bilər, çünki applicationId Android sistemində tətbiqin unikal identifikatorudur.
Arxitektur bölgü interface + flavor implementation üzərində qurulur. Əsas source set-də PaymentService interfeysi elan edilir. src/free/-də AdMob vasitəsilə ödənişdən əvvəl reklam göstərən tətbiq yerlışir. src/paid/-də isə dərhal ödəniş şlüzinə keçən tətbiq. PaymentService istifadə edən kod hansı tətbiqin yükləndiyini bilmir — bu, kompilyasiya mərhələsində həll olunur. Belə yanaşma zəmanət verir ki, proqramçı təsadüfən onu çağırsa belə, free versiyasına abunə idarəetmə kodu daxil olmayacaq.
Müxtəlif flavor-lar üçün APK ölçüsü asılılıqların daxil edilməsi/xaric edilməsi səbəbindən 5-15 MB fərqlənə bilər. Kitabxananı müəyyən flavor-dan xaric etmək üçün build.gradle-də flavor-specific dependencies istifadə olunur: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Bu asılılıq yalnız free variantı üçün əlavə ediləcək və paid versiyasının ölçüsünü artırmayacaq. Ümumi asılılıqlar üçün implementation istifadə olunur — onlar bütün flavor-lara daxil edilir.
// 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)
}
}
Çoxmodullu layihələrdə kitabxana modulları öz Product Flavors-na malik olmaya bilər, bu da problem yaradır: kitabxana bir dəfə (release kimi) qurulur, lakin flavor olan app modulu kitabxananın müvafiq variantını gözləyir. AGP 8.1-dən başlayaraq kitabxanalar publishing.multipleVariants bloku vasitəsilə multiple variants nəşr edə bilər — bu, kitabxananın bütün flavor variantlarını bir maven repozitoriyasına yayımlamağa imkan verir və app modulu avtomatik olaraq lazımını seçir.
Alternativ yanaşma — app modulunda olduğu kimi kitabxanada da eyni flavorDimensions və productFlavors elan etməkdir. AGP bir ölçüdə adın tam uyğunluğuna görə flavour-u avtomatik uyğumlaşdırır. Kitabxanada flavor adı app-dəki adla uyğun gəlirsə, AGP ardıcıl variantlar yaradacaq. Asan saxlanma üçün ümumi flavor təriflərinin Convention Plugin-də — layihənin bütün modullarına tətbiq edilən Gradle plaginində yerləşdirilməsi tövsiyə olunur.
Nəşr üçÿn nəzərdə tutulmayan kitabxanalar üçün (daxili modullar) kök layihənin build.gradle faylı vasitəsilə flavour-u sinxronizasiya etmək kifayətdir. Gradle bütün alt layihələrə konfiqurasiya tətbiq etməyə imkan verən subprojects metodunu təmin edir. Lakin subprojects-də çox böyük konfiqurasiyanın konfiqurasiya mərhələsini yavaşlatdığını unutmayın. Convention Plugins istifadə etmək tövsiyə olunur — onlar bir dəfə kompilyasiya olunur və təkrar istifadə edilir, bu da konfiqurasiya vaxtını 15-30% azaldır.
Tez-tez verilən suallar
Say məhdudiyyəti yoxdur, lakin hər ölçü Build Variants sayını artırır. Bir ölçüdə 4 flavour + 2 build types = 8 variant. İki ölçüdə 4 + 4 = 16 variant. Əçox 3 ölçü və 10-12 ümumi variant tövsiyə olunur.
Bəli, src/<flavor>/AndroidManifest.xml source set vasitəsilə. Manifest əsas ilə birləşir. Bütöv bloku əvəz etmək üçün tools:node="replace" istifadə edin. Məsələn, tətbiq etiketini və ya konkret flavor üçün icazələri dəyişdirmək.
<flavorName>Implementation konfiqurasiyasından istifadə edin. Nümunə: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Bu asılılıq yalnız free variantının qurulması zamanı daxil ediləcək. Paid üçün: paidImplementation. Ümumi asılılıqlar implementation vasitəsilə göstərilir.
Product Flavor məhsul versiyasını (free, paid, demo) müyyənləşdirir, Build Type — qurma metodunu (debug, release). Flavour applicationId, versionName və resursları əvəz edə bilər. Build Type debuggable, minification və signing-i idarə edir. Hər ikisi ortogonaldır və Build Variant-da birləşir.
Bəli, Product Flavors Compose ilə məhdudiyyətsiz işləyir. Müxtəlif flavor source sets və ya abstrakt siniflərin tətbiqi vasitəsilə fərqli Compose ekranlarına malik ola bilər. Flavor-specific Compose asılılıqları da əlavə etmək olar: freeImplementation 'androidx.compose.ui:ui-tooling'.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun