Build Variant در توسعه اندروید ترکیبی از build type و product flavor است که تعیین میکند APK یا AAB چگونه ساخته شود: با چه پارامترها، منابع و کدی. هر گزینه ساخت یک پیکربندی مجزای Gradle با applicationId، کلیدهای امضا و وابستگیهای خاص خود است. طبق Google Android Developers, 2025, پیکربندی صحیح Build Variants زمان ساخت را تا 40% کاهش میدهد با حذف منابع غیرضروری برای هر گزینه. سیستم گزینههای ساخت اساس مدیریت پیکربندی در پروژههای مدرن اندروید است.
نکات اصلی
Build Variant — نتیجه ترکیب یک Build Type و یک Product Flavor است. اگر در پروژه Product Flavor تعریف نشده باشد، Build Variant با Build Type منطبق میشود. Gradle به طور خودکار مجموعه کامل گزینهها را به عنوان حاصلضرب دکارتی تمام FlavorDimensions، Product Flavors و Build Types تولید میکند. به عنوان مثال، برای flavor free/paid و انواع debug/release، 8 گزینه ایجاد میشود: 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) به عنوان منسوخ شده علامتگذاری شده است. توصیه میشود از 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 فعال ترکیب میکند و به موارد خاص گزینه اولویت میدهد.
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/paid/release/res/ اولویت بیشتری دارد.
استفاده از source sets برای گزینهها — روش توصیه شده برای سفارشیسازی منابع. به جای بررسی BuildConfig.FLAVOR در کد و شاخهبندی منطق، میتوان به سادگی فایلهای مختلف را در source sets مختلف قرار داد. به عنوان مثال، آیکونهای نسخههای free و paid به ترتیب در src/free/res/ و src/paid/res/ قرار میگیرند، و AndroidManifest با مجوزهای مختلف — در src/free/AndroidManifest.xml و src/paid/AndroidManifest.xml. این تمیزتر، سریعتر (منابع کامپایل میشوند، نه در زمان اجرا بررسی) و ایمنتر است (نمیتوان به طور تصادفی قابلیت پرداختی را در نسخه رایگان به دلیل باگ در کد فعال کرد).
در پروژههای چندماژوله، هر ماژول (کتابخانه) میتواند 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 توصیه میکند flavorها را از طریق پیکربندی ساخت در پروژه ریشه با استفاده از 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 معنی ندارد (سرور mock نباید به محیط تولید راه یابد). Gradle variantFilter را ارائه میدهد — بلوک DSL که در آن میتوان ویژگیهای هر گزینه را بررسی کرد و آن را از طریق setIgnore(true) غیرفعال کرد. VariantFilter در مرحله پیکربندی، قبل از ایجاد وظایف اعمال میشود، بنابراین گزینه غیرفعال شده وظایف assemble و install را تولید نمیکند.
فیلتر کردن همچنین برای سرعتبخشی به ساخت مفید است. اگر پروژه 8 گزینه داشته باشد و توسعهدهنده فقط روی یکی کار کند، 7 گزینه دیگر همچنان از پیکربندی (configuration phase) عبور میکنند. با استفاده از variantFilter، گزینههای غیرفعال شده وظایفی ایجاد نمیکنند که زمان پیکربندی را برای پروژههای با 6+ بعد flavor 30-50% کاهش میدهد. در 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 استفاده میشود. در اندروید این مفهوم رسمیتر است و در Gradle تعبیه شده است.
بله، هر گزینه میتواند اندازه APK متفاوتی داشته باشد. ساختهای debug شامل اطلاعات اشکالزدایی، SDK و منابع پشتیبانینشده هستند. ساختهای release با کوچکسازی و کاهش منابع حداقل اندازه را میدهند. Product Flavor نیز تأثیر میگذارد: نسخه free بدون کتابخانههای پرداختی به اندازه این کتابخانهها از نسخه paid کوچکتر خواهد بود.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید