Build Variant — ما هو build type و product flavor في Android

المؤلف: IT Sectr نُشر: 2026-05-30 وقت القراءة: 9 دق

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 واحد.
  • Build Type يحدد وضع البناء: debug أو release.
  • Product Flavor يحدد إصدار التطبيق: free، paid، demo، enterprise.
  • Gradle يولد تلقائيًا مهام لكل Build Variant، بما في ذلك install و assemble.
  • الموارد والكود يمكن تجاوزها لكل متغير عبر مجموعات المصادر (source sets) المقابلة.

ما هو Build Variant؟

Build Variant هو نتيجة الجمع بين Build Type واحد و Product Flavor واحد. إذا لم يتم تعريف Product Flavors في المشروع، فإن Build Variant يطابق Build Type. يولد Gradle تلقائيًا المجموعة الكاملة من المتغيرات كمنتج ديكارتي لجميع FlavorDimensions و Product Flavors و Build Types. على سبيل المثال، للـ flavors 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% في المشاريع التي تحتوي على ثلاث بيئات نشر أو أكثر.

كيف يولد Gradle المتغيرات

AGP (Android Gradle Plugin) يحسب جميع التركيبات في مرحلة التكوين. إذا كان المشروع يحتوي على بُعدين مع flavorين وثلاثة flavors على التوالي، فسيقوم Gradle بإنشاء 2 × 2 × 3 = 12 تركيبة، مضروبة في عدد Build Types (عادة 2). تحصل كل تركيبة على اسم فريد ومجموعة مهام. يضيف AGP تلقائيًا source set لكل متغير: src/freeDebug/، src/paidRelease/، بالإضافة إلى src/free/ و src/debug/ العامة. أولوية قراءة الموارد: variant → flavor → type → main.

groovy
// مثال: 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 يحدد كيفية بناء التطبيق — مع أو بدون معلومات التصحيح، مع أو بدون تحسين، وبأي توقيع. Product Flavor يحدد ماذا نبني — أي إصدار من المنتج. Build Type هو آلية بناء (debug، release، staging). Product Flavor هو متغير منتج (free، paid، enterprise، demo). كلا المفهومين متعامدان: يمكن تطبيق أي Build Type على أي Product Flavor.

تتضمن Build Types الافتراضية debug (debuggable=true، minification=false، signing=debug.keystore) و release (debuggable=false، minification=true، signing=production.keystore). Product Flavor الافتراضي هو واحد، بدون اسم (فعليًا source set الرئيسي). يمكن للمطورين إضافة Build Types الخاصة بهم (مثل «staging» مع debuggable=true و minification=true) وأي عدد من Product Flavors. اختلاف آخر هو أنه لا يمكن تجميع Build Types في أبعاد، بينما يمكن لـ Product Flavors ذلك.

الفرق العملي الرئيسي: defaultConfig في build.gradle ينطبق على جميع Variants ولكن يمكن تجاوزه في productFlavors و buildTypes. حقل BuildConfigField المضاف إلى buildType يكون مرئيًا في جميع flavors من ذلك النوع، بينما المضاف إلى productFlavor يكون مرئيًا في جميع أنواع ذلك flavor. إذا تم تعريف حقل في كليهما، فإن buildType له الأولوية (يُطبق أخيرًا في السلسلة).

جدول المقارنة

الخاصيةBuild TypeProduct Flavor
الغرضكيفية البناءماذا نبني
أمثلةdebug، release، stagingfree، paid، demo، enterprise
الافتراضيdebug + releaseواحد (main)
Source Setsrc/debug/، src/release/src/free/، src/paid/
الأبعادلاflavorDimensions
ترتيب التطبيقبعد flavor، يتجاوزبعد defaultConfig
BuildConfigFieldيتجاوز flavorيتجاوز defaultConfig

تكوين Build Variants في build.gradle

أولوية التكوين

يتم تكوين Build Variants في كتلة android من ملف build.gradle على مستوى الوحدة. أولاً يتم تعريف buildTypes مع معلماتها، ثم flavorDimensions و productFlavors. يقوم Gradle تلقائيًا بإنشاء المتغيرات بناءً على هذه التعريفات. يرث كل متغير defaultConfig الخاص بالوحدة، مع تجاوز الحقول المحددة. يؤثر ترتيب التعريف على الأولوية: يتم تطبيق buildTypes بعد productFlavors.

للوصول إلى Build Variant معين في نصوص Gradle، استخدم android.applicationVariants (لوحدات التطبيق) أو android.libraryVariants (لوحدات المكتبات). هذه مجموعة يمكن التكرار عليها لتعديل تكوين كل متغير في وقت تشغيل التكوين. على سبيل المثال، يمكنك إضافة buildConfigField برمجيًا لجميع المتغيرات التي تحتوي على كلمة «demo».

Android Gradle Plugin 8.x أضاف دعمًا لـ onVariants — واجهة برمجية أنظف لتكوين المتغيرات عبر lambdas. تم وضع علامة على API القديم (variantOutput، variantFilter) كمهمل. يُوصى باستخدام onVariants مع onEach لوحدات المكتبات. الترحيل من variantOutput إلى onVariants هو خطوة موصى بها عند ترقية AGP من 7.x إلى 8.x.

groovy
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/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. هذا أنظف وأسرع (الموارد تُترجم، لا تُفحص في وقت التشغيل) وأكثر أمانًا (لا يمكنك تضمين وظائف مدفوعة عن طريق الخطأ في الإصدار المجاني بسبب خطأ في الكود).

Build Variant في المشاريع متعددة الوحدات

في المشاريع متعددة الوحدات، يمكن أن تحتوي كل وحدة (مكتبة) على Build Variants الخاصة بها. يقوم AGP بمزامنة المتغيرات تلقائيًا: إذا كانت وحدة التطبيق تبني paidRelease، فسيتم بناء جميع المكتبات التابعة أيضًا في متغيراتها المقابلة لـ paidRelease. تنشأ مشكلة عندما لا تحتوي المكتبة على product flavors ولكن وحدة التطبيق تحتوي — عندها تُبنى المكتبة مرة واحدة (release أو debug حسب النوع).

لوحدات المكتبات، يتطابق Build Variant افتراضيًا مع Build Type لوحدة التطبيق، حيث أن المكتبات لا تحتوي على product flavors. إذا كانت المكتبة تحتاج إلى التكيف مع flavor وحدة التطبيق، فيجب تعريف نفس flavorDimensions و productFlavors في المكتبة. يطابق AGP flavors حسب تطابق الاسم التام. يوصي Gradle بمزامنة flavors من خلال تكوين البناء في المشروع الجذري باستخدام subprojects أو Convention Plugins.

بدءًا من AGP 8.1، يمكن للمكتبات نشر متغيرات متعددة — نشر جميع متغيرات المكتبة إلى مستودع maven في وقت واحد. هذا يحل المشكلة عندما تستخدم وحدة التطبيق flavor مدفوع ولكن المكتبة منشورة فقط للإصدار المجاني. نشر المتغيرات المتعددة (MVP) يسمح للمشروع التابع بتحديد المتغير المطلوب تلقائيًا. لتفعيل MVP، أضف publishing { multipleVariants { ... } } إلى build.gradle الخاص بالمكتبة.

تصفية وتعطيل المتغيرات

التصفية الديناميكية عبر CI/CD

أحيانًا يكون من الضروري تعطيل بعض Build Variants — على سبيل المثال، إذا كان الجمع mockRelease لا معنى له (خادم mock لا ينبغي أن يذهب إلى الإنتاج). يوفر Gradle variantFilter — كتلة DSL حيث يمكنك التحقق من خصائص كل متغير وتعطيله عبر setIgnore(true). يُطبق VariantFilter في مرحلة التكوين، قبل إنشاء المهام، لذلك لا يولد المتغير المعطل مهام assemble و install.

التصفية مفيدة أيضًا لتسريع البناء. إذا كان المشروع يحتوي على 8 متغيرات ولكن المطور يعمل على متغير واحد فقط، فإن المتغيرات السبعة المتبقية لا تزال تمر عبر التكوين. عند استخدام variantFilter، لا تنشئ المتغيرات المعطلة مهامًا، مما يقلل وقت التكوين بنسبة 30-50% للمشاريع التي تحتوي على 6+ أبعاد flavor. في CI/CD، يمكنك تصفية المتغيرات ديناميكيًا عبر معاملات سطر الأوامر -PbuildOnly=paidRelease.

groovy
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)
    }
}

الأسئلة الشائعة

كم عدد Build Variants التي يمكن إنشاؤها؟

لا يوجد حد، لكن Gradle ينشئ المنتج الديكارتي لجميع flavors والأنواع. إذا كان لديك 3 أبعاد مع 3 flavors لكل منها و 3 build types، تحصل على 27 متغيرًا. كثرة المتغيرات تبطئ التكوين. يُوصى بألا يزيد عدد المتغيرات عن 10-12 في وحدة واحدة.

لماذا نحتاج flavorDimensions؟

flavorDimensions تجمع Product Flavors في محاور مستقلة. على سبيل المثال، البعد «tier» (free، paid) والبعد «region» (us، eu). بدون أبعاد، تنتمي جميع flavors إلى محور واحد، وسيختار Gradle flavor واحدًا فقط من الكل (لا يمكن أن يكون لديك free+us و paid+eu كمتغيرين منفصلين).

كيفية تجاوز applicationId لمتغير؟

في كتلة productFlavor أو buildType، حدد applicationId. على سبيل المثال، للإصدار المجاني: free { applicationId «com.example.app.free» }. في البيان، استخدم ${applicationId} — سيقوم Gradle تلقائيًا باستبدال القيمة. هذا يسمح بتثبيت كلا المتغيرين على جهاز واحد.

هل يمكن استخدام Build Variants في iOS؟

في iOS، ما يعادل Build Variants هو مزيج Scheme + Configuration. يتم تكوين Xcode Schemes من خلال تكوينات Debug/Release بمعاملات مختلفة. للإصدارات المتعددة (free/paid)، تُستخدم Build Configurations و Preprocessor Macros. في Android، المفهوم أكثر رسمية ومضمن في Gradle.

هل يؤثر Build Variant على حجم APK؟

نعم، يمكن أن يكون لكل متغير حجم APK مختلف. تتضمن بناءات debug معلومات التصحيح و SDK و الموارد غير المدعومة. بناءات release مع minification و resource shrinking تنتج الحد الأدنى للحجم. Product Flavor يؤثر أيضًا على الحجم: الإصدار المجاني بدون مكتبات مدفوعة سيكون أصغر من الإصدار المدفوع بحجم تلك المكتبات.

الخلاصة

  • Build Variant — مزيج من Build Type واحد و Product Flavor واحد يحدد تكوين البناء.
  • Build Type يتحكم في وضع الترجمة (debug/release/staging)، بينما Product Flavor يتحكم في إصدار المنتج (free/paid).
  • مجموعات المصادر تسمح بتجاوز الكود والموارد والملف البياني لكل متغير بناء.
  • VariantFilter يعطل التركيبات غير الضرورية، مما يسرع تكوين Gradle بنسبة 30-50%.
  • المشاريع متعددة الوحدات تتطلب مزامنة flavors عبر جميع الوحدات أو نشر متغيرات متعددة.
  • BuildConfigField ومجموعات المصادر هما طريقتان نظيفتان لتخصيص السلوك بين المتغيرات.
  • توصية: لا تنشئ أكثر من 10-12 متغيرًا في مشروع واحد؛ قم بتجميع الأبعاد بشكل ذي معنى.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا