ضبط صيغ الإصدار

توضّح هذه الصفحة كيفية ضبط صيغ الإنشاء لإنشاء إصدارات مختلفة من تطبيقك من مشروع واحد، وكيفية إدارة التبعيات وإعدادات التوقيع بشكل سليم.

يمثّل كل نوع إصدار إصدارًا مختلفًا من تطبيقك يمكنك إنشاؤه. على سبيل المثال، قد تريد إنشاء إصدار مجاني من تطبيقك يتضمّن مجموعة محدودة من المحتوى، وإصدار آخر مدفوع يتضمّن المزيد من المحتوى. يمكنك أيضًا إنشاء إصدارات مختلفة من تطبيقك تستهدف أجهزة مختلفة، استنادًا إلى مستوى واجهة برمجة التطبيقات أو اختلافات أخرى في الأجهزة.

تنتج صيغ الإنشاء عن استخدام Gradle لمجموعة معيّنة من القواعد لدمج الإعدادات والرموز والموارد التي تم ضبطها في أنواع الإنشاء ونكهات المنتجات. على الرغم من أنّك لا تضبط إعدادات صيغ الإنشاء مباشرةً، يمكنك ضبط إعدادات أنواع الإنشاء ونكهات المنتجات التي تتكوّن منها.

على سبيل المثال، قد يحدّد نوع المنتج "تجريبي" ميزات ومتطلبات جهاز معيّنة، مثل رمز المصدر المخصّص والموارد والحد الأدنى لمستويات واجهة برمجة التطبيقات، بينما يطبّق نوع الإصدار "تصحيح الأخطاء" إعدادات مختلفة للإصدار والتعبئة، مثل خيارات تصحيح الأخطاء ومفاتيح التوقيع. إنّ صيغة الإنشاء التي تجمع بين هذين النوعين هي إصدار "demoDebug" من تطبيقك، وهي تتضمّن مجموعة من الإعدادات والموارد المضمّنة في نكهة المنتج "demo" ونوع الإصدار "debug" ومجموعة المصادر main/.

ضبط أنواع التصميمات

يمكنك إنشاء أنواع الإصدارات وإعدادها داخل حزمة android في ملف build.gradle.kts على مستوى الوحدة. عند إنشاء وحدة جديدة، ينشئ "استوديو Android" تلقائيًا نوعَي الإصدارين التجريبي والإصدار العلني. على الرغم من أنّ نوع الإصدار المخصّص لتصحيح الأخطاء لا يظهر في ملف إعداد الإصدار، يضبطه Android Studio باستخدام debuggable true. يتيح لك ذلك تصحيح أخطاء التطبيق على أجهزة Android الآمنة، كما يتيح إعداد توقيع التطبيق باستخدام ملف تخزين مفاتيح تصحيح الأخطاء العام.

يمكنك إضافة نوع الإصدار المخصّص لتصحيح الأخطاء إلى إعداداتك إذا أردت إضافة إعدادات معيّنة أو تغييرها. يحدّد المثال التالي applicationIdSuffix لنوع الإصدار المخصّص لتصحيح الأخطاء ويضبط نوع إصدار "مرحلة تجريبية" يتم إعداده باستخدام الإعدادات من نوع الإصدار المخصّص لتصحيح الأخطاء:

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

Groovy

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

ملاحظة: عند إجراء تغييرات على ملف إعدادات الإصدار، يطلب منك Android Studio مزامنة مشروعك مع الإعدادات الجديدة. لمزامنة مشروعك، انقر على المزامنة الآن في شريط الإشعارات الذي يظهر عند إجراء تغيير أو النقر على مزامنة المشروع من شريط الأدوات. إذا رصد Android Studio أي أخطاء في عملية الإعداد، ستظهر نافذة الرسائل لوصف المشكلة.

لمزيد من المعلومات عن جميع المواقع التي يمكنك ضبطها باستخدام أنواع الإصدارات، يمكنك الاطّلاع على مرجع BuildType.

ضبط إعدادات نكهات المنتجات

تشبه عملية إنشاء متغيرات المنتج عملية إنشاء أنواع الإصدارات. أضِف نكهات المنتج إلى الكتلة productFlavors في إعدادات الإصدار، وأدرِج الإعدادات التي تريدها. تتيح إصدارات المنتج استخدام الخصائص نفسها التي تتيحها defaultConfig، لأنّ defaultConfig تنتمي في الواقع إلى فئة ProductFlavor. وهذا يعني أنّه يمكنك توفير إعدادات أساسية لجميع الأنواع في الحزمة defaultConfig، ويمكن لكل نوع تغيير أي من هذه القيم التلقائية، مثل applicationId. لمزيد من المعلومات حول معرّف التطبيق، يمكنك الاطّلاع على مقالة ضبط معرّف التطبيق.

ملاحظة: سيظل عليك تحديد اسم حزمة باستخدام السمة package في ملف البيان main/. يجب أيضًا استخدام اسم الحزمة هذا في الرمز المصدر للإشارة إلى فئة R أو لحل أي تسجيل نسبي للنشاط أو الخدمة. يتيح لك ذلك استخدام applicationId لمنح كل إصدار من المنتج معرّفًا فريدًا للتغليف والتوزيع بدون الحاجة إلى تغيير الرمز المصدري.

يجب أن تنتمي جميع النكهات إلى فئة نكهات محدّدة الاسم، وهي مجموعة من نكهات المنتجات. يجب تعيين جميع الأنواع إلى سمة نوع المنتج، وإلا سيظهر خطأ الإنشاء التالي.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

إذا حدّدت وحدة معيّنة سمة واحدة فقط من سمات الإصدار، يحدّد مكوّن Android الإضافي في Gradle تلقائيًا جميع إصدارات الوحدة لهذه السمة.

ينشئ نموذج الرمز البرمجي التالي سمة إصدار باسم "version" ويضيف نكهتَي منتج "demo" و "full". توفّر هذه النكهات وapplicationIdSuffix وversionNameSuffix الخاصة بها:

Kotlin

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

Groovy

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

ملاحظة: إذا كان لديك تطبيق قديم (تم إنشاؤه قبل آب (أغسطس) 2021) وتوزّعه باستخدام حِزم APK على Google Play، عليك اتّباع الخطوات التالية لتوزيعه باستخدام ميزة "حِزم APK المتعددة" على Google Play: تعيين القيمة نفسها applicationId لجميع حِزم APK المتغيرة، وتعيين قيمة versionCode مختلفة لكل حزمة APK متغيرة. لتوزيع إصدارات مختلفة من تطبيقك كتطبيقات منفصلة على Google Play، عليك تعيين applicationId مختلف لكل إصدار.

بعد إنشاء نُسخ المنتج وإعدادها، انقر على المزامنة الآن في شريط الإشعارات. بعد اكتمال المزامنة، ينشئ Gradle تلقائيًا صيغًا متعددة للإنشاء استنادًا إلى أنواع الإنشاء ونكهات المنتجات، ويسمّيها وفقًا <product-flavor><Build-Type>. على سبيل المثال، إذا أنشأت نكهتَي المنتج "تجريبية" و "كاملة"، واحتفظت بنوعَي الإصدار "تصحيح الأخطاء" و"إصدار" التلقائيين، سينشئ Gradle متغيرات الإصدار التالية:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

لاختيار صيغة الإصدار التي تريد إنشاءها وتشغيلها، انتقِل إلى إنشاء > اختيار صيغة الإصدار واختَر صيغة إصدار من القائمة. لبدء تخصيص كل صيغة من صيغ البناء بميزاتها ومواردها الخاصة، عليك إنشاء مجموعات مصادر وإدارتها، كما هو موضّح في هذه الصفحة.

تغيير معرّف التطبيق لخيارات الإنشاء

عند إنشاء حزمة APK أو AAB لتطبيقك، تضع أدوات الإنشاء علامة على التطبيق باستخدام معرّف التطبيق المحدّد في الحظر defaultConfig من الملف build.gradle.kts، كما هو موضّح في المثال التالي. ومع ذلك، إذا أردت إنشاء إصدارات مختلفة من تطبيقك لتظهر كبطاقات بيانات منفصلة على "متجر Google Play"، مثل إصدار "مجاني" وإصدار "احترافي"، عليك إنشاء أنواع إصدارات منفصلة لكل منها معرّف تطبيق مختلف.

في هذه الحالة، حدِّد كل صيغة من صيغ الإنشاء باعتبارها نكهة منتج منفصلة. بالنسبة إلى كل صيغة داخل الحظر productFlavors، يمكنك إعادة تعريف السمة applicationId، أو يمكنك بدلاً من ذلك إلحاق مقطع بمعرّف التطبيق التلقائي باستخدام applicationIdSuffix، كما هو موضّح هنا:

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

بهذه الطريقة، يكون معرّف التطبيق لنوع المنتج "المجاني" هو "com.example.myapp.free".

يمكنك أيضًا استخدام applicationIdSuffix لإلحاق شريحة استنادًا إلى نوع الإصدار، كما هو موضّح هنا:

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

بما أنّ Gradle يطبّق إعدادات نوع الإصدار بعد نوع المنتج، سيكون معرّف التطبيق لمتغير الإصدار "تصحيح الأخطاء المجاني" هو "com.example.myapp.free.debug". ويكون ذلك مفيدًا عندما تريد توفير كل من إصدار التصحيح وإصدار التطبيق على الجهاز نفسه، لأنّه لا يمكن أن يتضمّن تطبيقان معرّف التطبيق نفسه.

إذا كان لديك تطبيق قديم (تم إنشاؤه قبل أغسطس 2021) توزّعه باستخدام حِزم APK على Google Play، وأردت استخدام بطاقة بيانات المتجر نفسها لتوزيع حِزم APK متعددة يستهدف كل منها إعدادات جهاز مختلفة، مثل مستوى واجهة برمجة التطبيقات، عليك استخدام معرّف التطبيق نفسه لكل صيغة من صيغ الإصدار، ولكن يجب منح كل حزمة APK versionCode مختلفًا. لمزيد من المعلومات، اطّلِع على مقالة التوافق مع حِزم APK المتعددة. لا يتأثر النشر باستخدام حِزم AAB، لأنّه يستخدم عنصرًا واحدًا يستخدِم رمز إصدار واحدًا ومعرّف تطبيق واحدًا تلقائيًا.

ملاحظة: إذا كنت بحاجة إلى الإشارة إلى معرّف التطبيق في ملف البيان، يمكنك استخدام العنصر النائب ${applicationId} في أي سمة من سمات البيان. أثناء عملية الإنشاء، يستبدل Gradle هذه العلامة بمعرّف التطبيق الفعلي. لمزيد من المعلومات، يُرجى الاطّلاع على إدراج متغيرات الإنشاء في ملف البيان.

الجمع بين نكهات منتجات متعددة وسمات النكهة

في بعض الحالات، قد تحتاج إلى دمج عمليات الضبط من عدة نكهات منتجات. على سبيل المثال، قد تحتاج إلى إنشاء إعدادات مختلفة لنكهتَي المنتج "كامل" و "تجريبي" استنادًا إلى مستوى واجهة برمجة التطبيقات. ولإجراء ذلك، يتيح لك المكوّن الإضافي لنظام Android المتوافق مع Gradle إنشاء مجموعات متعدّدة من متغيرات المنتج كسمات للمتغيرات.

عند إنشاء تطبيقك، يجمع Gradle إعدادات نوع المنتج من كل سمة تحدّدها، بالإضافة إلى إعدادات نوع الإصدار، لإنشاء صيغة الإصدار النهائية. لا يجمع Gradle بين نكهات المنتجات التي تنتمي إلى سمة النكهة نفسها.

يستخدم نموذج الرمز البرمجي التالي السمة flavorDimensions لإنشاء سمة "الوضع" الخاصة بنوع الإصدار لتجميع أنواع الإصدارات "الكاملة" و "التجريبية"، وسمة "api" الخاصة بنوع الإصدار لتجميع إعدادات أنواع الإصدارات استنادًا إلى مستوى واجهة برمجة التطبيقات:

Kotlin

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.

      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion 23
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion 21
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

إنّ عدد صيغ الإنشاء التي ينشئها Gradle يساوي ناتج ضرب عدد النكهات في كل سمة من سمات النكهة وعدد أنواع الإنشاء التي يمكنك ضبطها. عندما يحدّد Gradle أسماء كل صيغة إنشاء أو العناصر ذات الصلة، تظهر أولاً نكهات المنتجات التي تنتمي إلى سمة النكهة ذات الأولوية الأعلى، تليها تلك التي تنتمي إلى سمات ذات أولوية أقل، ثم نوع الإصدار.

باستخدام إعدادات الإصدار السابقة كمثال، ينشئ Gradle ما مجموعه 12 صيغة إصدار باستخدام نظام التسمية التالي:

  • تنويعة الإصدار: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • حزمة APK المطابقة: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • على سبيل المثال:
    نوع الإصدار: minApi24DemoDebug
    حِزمة APK المطابقة: app-minApi24-demo-debug.apk

بالإضافة إلى أدلة مجموعات المصادر التي يمكنك إنشاؤها لكل إصدار منتج فردي ومتغير إصدار، يمكنك أيضًا إنشاء أدلة مجموعات المصادر لكل مجموعة من إصدارات المنتجات. على سبيل المثال، يمكنك إنشاء مصادر Java وإضافتها إلى الدليل src/demoMinApi24/java/، ولا يستخدم Gradle هذه المصادر إلا عند إنشاء صيغة تجمع بين نوعَي المنتجَين هذين.

تكون لمجموعات المصادر التي تنشئها لمجموعات نكهات المنتجات أولوية أعلى من مجموعات المصادر التي تنتمي إلى كل نكهة منتج فردية. لمزيد من المعلومات حول مجموعات المصادر وكيفية دمج Gradle للموارد، يمكنك الاطّلاع على القسم حول كيفية إنشاء مجموعات المصادر.

فلترة خيارات المنتجات

ينشئ Gradle صيغة إنشاء لكل تركيبة ممكنة من نكهات المنتجات وأنواع الإصدارات التي يتم ضبطها. ومع ذلك، قد تكون هناك بعض صيغ الإصدار التي لا تحتاج إليها أو التي لا تبدو منطقية في سياق مشروعك. لإزالة بعض إعدادات صيغة الإنشاء، أنشئ فلترًا للصيغة في ملف build.gradle.kts على مستوى الوحدة.

باستخدام إعدادات الإصدار من القسم السابق كمثال، لنفترض أنّك تخطّط لتوفير الإصدار التجريبي من التطبيق على المستوى 23 من واجهة برمجة التطبيقات والإصدارات الأحدث فقط. يمكنك استخدام الحظر variantFilter لتصفية جميع إعدادات صيغ الإصدار التي تجمع بين نكهتَي المنتج "minApi21" و "demo":

Kotlin

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("api", "mode")
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enable = false
        }
    }
}
...

Groovy

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

بعد إضافة فلتر صيغة إلى إعدادات الإصدار والنقر على المزامنة الآن في شريط الإشعارات، يتجاهل Gradle أي صيغ إصدار تستوفي الشروط التي تحدّدها. لم تعُد خيارات الإصدار تظهر في القائمة عند النقر على Build > Select Build Variant من شريط القوائم أو Build Variants في شريط نافذة الأدوات.

إنشاء مجموعات مصادر

يُنشئ Android Studio تلقائيًا main/ مجموعة المصادر والأدلة لكل ما تريد مشاركته بين جميع صيغ البناء. ومع ذلك، يمكنك إنشاء مجموعات مصادر جديدة للتحكّم بدقة في الملفات التي يجمعها Gradle ويحزّمها لأنواع إصدارات معيّنة، ونكهات منتجات، ومجموعات من نكهات المنتجات (عند استخدام سمات النكهة)، ومتغيرات الإصدار.

على سبيل المثال، يمكنك تحديد الوظائف الأساسية في مجموعة المصادر main/ واستخدام مجموعات مصادر إصدارات المنتجات لتغيير العلامة التجارية لتطبيقك لعملاء مختلفين، أو تضمين أذونات خاصة ووظائف تسجيل الدخول فقط لمتغيرات الإصدار التي تستخدم نوع الإصدار المخصّص لتصحيح الأخطاء.

يتوقّع Gradle أن يتم تنظيم ملفات ومجلدات مجموعات المصادر بطريقة معيّنة، على غرار مجموعة المصادر main/. على سبيل المثال، يتوقّع Gradle أن تكون ملفات فئات Kotlin أو Java الخاصة بنوع الإصدار "تصحيح الأخطاء" موجودة في الدليلين src/debug/kotlin/ أو src/debug/java/.

يوفّر المكوّن الإضافي لنظام Gradle المتوافق مع Android مهمة Gradle مفيدة توضّح لك كيفية تنظيم ملفاتك لكل نوع من أنواع الإصدارات ونكهات المنتجات ومتغيرات الإصدار. على سبيل المثال، يوضّح النموذج التالي من ناتج المهمة المكان الذي يتوقّع Gradle العثور فيه على ملفات معيّنة لنوع الإصدار "debug":

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

لعرض هذا الناتج، اتّبِع الخطوات التالية:

  1. انقر على Gradle في شريط نافذة الأدوات.
  2. انتقِل إلى MyApplication > المهام > android وانقر مرتَين على sourceSets.

    لعرض مجلد المهام، يجب السماح لـ Gradle بإنشاء قائمة المهام أثناء المزامنة. ولإجراء ذلك، اتبع الخطوات التالية:

    1. انقر على ملف (File) > الإعدادات (Settings) > تجريبي (Experimental) (استوديو Android (Android Studio) > الإعدادات (Settings) > تجريبي (Experimental) في نظام التشغيل macOS).
    2. ألغِ تحديد عدم إنشاء قائمة مهام Gradle أثناء مزامنة Gradle.
  3. بعد أن ينفّذ Gradle المهمة، تفتح نافذة تشغيل لعرض الناتج.

ملاحظة: يوضّح لك ناتج المهمة أيضًا كيفية تنظيم مجموعات المصادر للملفات التي تريد استخدامها لتنفيذ اختبارات لتطبيقك، مثل test/ وandroidTest/ مجموعات مصادر الاختبار.

عند إنشاء صيغة جديدة من إصدارات التطبيق، لا ينشئ Android Studio لك أدلة مجموعة المصادر، ولكنّه يوفّر لك بعض الخيارات لمساعدتك. على سبيل المثال، لإنشاء الدليل java/ فقط لنوع الإصدار "تصحيح الأخطاء"، اتّبِع الخطوات التالية:

  1. افتح لوحة المشروع واختَر عرض المشروع من القائمة في أعلى اللوحة.
  2. الانتقال إلى MyProject/app/src/
  3. انقر بزر الماوس الأيمن على الدليل src واختَر جديد > دليل.
  4. من القائمة ضمن مجموعات مصادر Gradle، اختَر full/java.
  5. اضغط على Enter.

ينشئ "استوديو Android" دليل مجموعة مصادر لنوع الإصدار المخصّص لتصحيح الأخطاء، ثم ينشئ الدليل java/ داخله. بدلاً من ذلك، يمكن أن ينشئ &quot;استوديو Android&quot; الأدلة نيابةً عنك عند إضافة ملف جديد إلى مشروعك لمتغير إصدار معيّن.

على سبيل المثال، لإنشاء ملف XML للقيم لنوع الإصدار "debug"، اتّبِع الخطوات التالية:

  1. في لوحة المشروع، انقر بزر الماوس الأيمن على الدليل src واختَر جديد > XML > ملف XML للقيم.
  2. أدخِل اسمًا لملف XML أو احتفظ بالاسم التلقائي.
  3. من القائمة بجانب مجموعة المصادر المستهدَفة، اختَر تصحيح الأخطاء.
  4. انقر على إنهاء.

بما أنّه تم تحديد نوع الإصدار "debug" كمجموعة المصادر المستهدَفة، ينشئ &quot;استوديو Android&quot; تلقائيًا الأدلة اللازمة عند إنشاء ملف XML. يبدو هيكل الدليل الناتج كما هو موضح في الشكل 1.

الشكل 1. أدلة جديدة لمجموعة المصادر لنوع الإصدار "تصحيح الأخطاء".

تحتوي مجموعات المصادر النشطة على مؤشر أخضر في الرمز الخاص بها للإشارة إلى أنّها نشطة. يتم إلحاق اللاحقة [main] بمجموعة رموز المصدر debug للإشارة إلى أنّه سيتم دمجها في مجموعة رموز المصدر main.

باستخدام الإجراء نفسه، يمكنك أيضًا إنشاء أدلة لمجموعات المصادر خاصة بنكهات المنتجات، مثل src/demo/، ومتغيرات الإصدارات، مثل src/demoDebug/. بالإضافة إلى ذلك، يمكنك إنشاء مجموعات مصادر اختبار تستهدف صيغًا معيّنة من الإصدارات، مثل src/androidTestDemoDebug/. لمزيد من المعلومات، اطّلِع على مقالة اختبار مجموعات المصادر.

تغيير إعدادات مجموعة المصادر التلقائية

إذا كانت لديك مصادر غير منظَّمة في بنية ملف مجموعة المصادر التلقائية التي يتوقّعها Gradle، كما هو موضّح في القسم السابق حول إنشاء مجموعات المصادر، يمكنك استخدام الحظر sourceSets لتغيير المكان الذي يبحث فيه Gradle لجمع الملفات لكل مكوّن من مكوّنات مجموعة المصادر.

يجب أن تكون كتلة sourceSets ضمن كتلة android. لست بحاجة إلى نقل ملفات المصدر، بل عليك فقط تزويد Gradle بالمسارات ذات الصلة بملف build.gradle.kts على مستوى الوحدة، حيث يمكن لـ Gradle العثور على ملفات لكل مكوّن من مكوّنات مجموعة المصادر. لمعرفة المكوّنات التي يمكنك ضبطها وما إذا كان بإمكانك ربطها بملفات أو أدلة متعددة، راجِع مرجع واجهة برمجة التطبيقات الخاصة بمكوّن Android Gradle الإضافي.

يوضّح نموذج الرمز التالي كيفية ربط المصادر من الدليل app/other/ ببعض مكوّنات مجموعة المصادر main وتغيير الدليل الجذر لمجموعة المصادر androidTest:

Kotlin

android {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("androidTest") {
      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
  }
}
...

Groovy

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

يُرجى العِلم أنّه لا يمكن أن ينتمي دليل مصدر إلا إلى مجموعة مصادر واحدة. على سبيل المثال، لا يمكنك مشاركة مصادر الاختبار نفسها مع كلّ من مجموعتَي المصادر test وandroidTest. ويرجع ذلك إلى أنّ &quot;استوديو Android&quot; ينشئ وحدات IntelliJ منفصلة لكل مجموعة مصادر ولا يمكنه توفير جذور محتوى مكرّرة في مجموعات المصادر.

إنشاء مجموعات رموز المصدر

يمكنك استخدام أدلة مجموعات المصادر لاحتواء الرموز والموارد التي تريد تضمينها فقط في حِزم ذات إعدادات معيّنة. على سبيل المثال، إذا كنت بصدد إنشاء صيغة البناء "demoDebug"، وهي المنتج المشترك بين نكهة المنتج "demo" ونوع البناء "debug"، سيبحث Gradle في هذه الدلائل ويمنحها الأولوية التالية:

  1. src/demoDebug/ (مجموعة مصادر تنويعة التصميم)
  2. src/debug/ (مجموعة مصادر نوع الإصدار)
  3. src/demo/ (مجموعة مصادر صيغ المنتجات)
  4. src/main/ (مجموعة المصادر الرئيسية)

يجب أن تتضمّن مجموعات المصادر التي تم إنشاؤها لتركيبات من نكهات المنتجات جميع سمات النكهات. على سبيل المثال، يجب أن تكون مجموعة مصادر صيغة الإنشاء هي مزيج من نوع الإنشاء وجميع سمات النكهة. لا يمكن دمج الرموز والموارد التي تتضمّن مجلدات تغطي سمات متعددة ولكن ليس كلها.

في حال دمج نكهات منتجات متعدّدة، يتم تحديد الأولوية بين نكهات المنتجات حسب سمة النكهة التي تنتمي إليها. عند إدراج سمات النكهة باستخدام السمة android.flavorDimensions، تكون لنكهات المنتجات التي تنتمي إلى سمة النكهة الأولى التي تدرجها أولوية أعلى من تلك التي تنتمي إلى سمة النكهة الثانية، وهكذا. بالإضافة إلى ذلك، تكون لمجموعات المصادر التي تنشئها لمجموعات من نكهات المنتجات أولوية أعلى من مجموعات المصادر التي تخص نكهة منتج فردية.

يحدّد ترتيب الأولوية مجموعة المصادر التي لها أولوية أعلى عندما يجمع Gradle بين الرموز والموارد. بما أنّ دليل مجموعة المصادر demoDebug/ يحتوي على الأرجح على ملفات خاصة بمتغير الإصدار هذا، إذا كان demoDebug/ يتضمّن ملفًا معرَّفًا أيضًا في debug/، سيستخدم Gradle الملف في مجموعة المصادر demoDebug/. وبالمثل، يمنح Gradle الملفات في مجموعات المصادر الخاصة بنوع الإصدار ونكهة المنتج أولوية أعلى من الملفات نفسها في main/. يأخذ Gradle ترتيب الأولوية هذا في الاعتبار عند تطبيق قواعد الإنشاء التالية:

  • يتم تجميع كل رموز المصدر في الدليلين kotlin/ أو java/ معًا لإنشاء ناتج واحد.

    ملاحظة: بالنسبة إلى صيغة إصدار معيّنة، يعرض Gradle خطأ في الإصدار إذا صادف دليلَين أو أكثر لمجموعات المصادر تم فيهما تحديد فئة Kotlin أو Java نفسها. على سبيل المثال، عند إنشاء تطبيق تصحيح الأخطاء، لا يمكنك تحديد كل من src/debug/Utility.kt وsrc/main/Utility.kt، لأنّ Gradle يبحث في كلا الدليلين أثناء عملية الإنشاء ويُظهر الخطأ "فئة مكرّرة". إذا كنت تريد إصدارات مختلفة من Utility.kt لأنواع إصدارات مختلفة، يجب أن يحدّد كل نوع إصدار نسخته الخاصة من الملف وألا يدرجه في مجموعة المصادر main/.

  • يتم دمج ملفات البيان معًا في ملف بيان واحد. يتم منح الأولوية بالترتيب نفسه الوارد في القائمة في المثال السابق. أي أنّ إعدادات البيان لنوع الإصدار تتجاوز إعدادات البيان لمتغير المنتج، وهكذا. لمزيد من المعلومات، اطّلِع على مقالة دمج ملفات البيان.
  • يتم دمج الملفات في أدلة values/ معًا. إذا كان ملفان يحملان الاسم نفسه، مثل ملفَي strings.xml، يتم منح الأولوية بالترتيب نفسه كما في القائمة في المثال السابق. أي أنّ القيم المحدّدة في ملف في مجموعة مصادر نوع الإصدار تتجاوز القيم المحدّدة في الملف نفسه في صيغة المنتج، وهكذا.
  • يتم تجميع الموارد في الدليلَين res/ وasset/ معًا. إذا كانت هناك موارد تحمل الاسم نفسه محدّدة في مجموعتَي مصادر أو أكثر، يتم منح الأولوية بالترتيب نفسه كما في المثال السابق.
  • يمنح Gradle الموارد وبيانات البيان المضمّنة في تبعيات وحدة المكتبة الأولوية الأدنى عند إنشاء التطبيق.

تعريف العناصر التابعة

لضبط تبعية لمتغير إصدار معيّن أو مجموعة مصادر اختبار، أضِف بادئة لاسم متغير الإصدار أو مجموعة مصادر الاختبار قبل الكلمة الرئيسية Implementation، كما هو موضّح في المثال التالي:

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

لمزيد من المعلومات حول ضبط التبعيات، يُرجى الاطّلاع على إضافة تبعيات الإصدار.

استخدام إدارة التبعية المتوافقة مع صيغ المنتج

يتضمّن الإصدار 3.0.0 والإصدارات الأحدث من Android Gradle Plugin آلية جديدة للبرامج الاعتمادية تعمل على مطابقة الصيغ تلقائيًا عند استخدام مكتبة. وهذا يعني أنّ صيغة debug لأحد التطبيقات تستخدم تلقائيًا صيغة debug لإحدى المكتبات، وهكذا. يعمل هذا الإجراء أيضًا عند استخدام نُسخ مختلفة: ستستخدم النسخة freeDebug من التطبيق النسخة freeDebug من المكتبة.

لكي تتطابق الإضافات مع البدائل بشكل دقيق، عليك توفير بدائل مطابقة كما هو موضّح في القسم التالي، وذلك في الحالات التي لا يمكن فيها إجراء مطابقة مباشرة.

على سبيل المثال، لنفترض أنّ تطبيقك يضبط نوع إصدار باسم "مرحلة تجريبية"، ولكن أحد العناصر التابعة للمكتبة لا يضبطه. عندما تحاول الإضافة إنشاء إصدار "مرحلة تجريبية" من تطبيقك، لن تعرف الإضافة إصدار المكتبة الذي يجب استخدامه، وستظهر لك رسالة خطأ مشابهة لما يلي:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

حلّ أخطاء الإنشاء المتعلّقة بمطابقة صيغ التطبيق

تتضمّن المكوّن الإضافي عناصر خاصة بلغة DSL لمساعدتك في التحكّم في طريقة حل Gradle للحالات التي لا يمكن فيها إجراء مطابقة مباشرة بين تطبيق وتبعيات.

في ما يلي قائمة بالمشاكل المتعلّقة بمطابقة التبعيات المتوافقة مع صيغ المنتج وكيفية حلّها باستخدام خصائص DSL:

  • يتضمّن تطبيقك نوع إصدار لا يتضمّنه عنصر تابع لمكتبة.

    على سبيل المثال، يتضمّن تطبيقك نوع الإصدار "staging"، ولكن لا تتضمّن إحدى التبعيات سوى نوعَي الإصدار "debug" و "release".

    يُرجى العِلم أنّه لا توجد مشكلة عندما يتضمّن عنصر تابع لمكتبة نوع إصدار لا يتضمّنه تطبيقك. ويرجع ذلك إلى أنّ المكوّن الإضافي لا يطلب أبدًا نوع الإصدار هذا من التبعية.

    استخدِم matchingFallbacks لتحديد مطابقات بديلة لنوع إصدار معيّن، كما هو موضّح هنا:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
  • بالنسبة إلى سمة إصدار معيّن متوفّرة في كل من التطبيق والمكتبة التي يعتمد عليها، يتضمّن تطبيقك إصدارات لا تتضمّنها المكتبة.

    على سبيل المثال، يتضمّن كلّ من تطبيقك والعناصر الاعتمادية للمكتبة سمة "المستوى". ومع ذلك، يتضمّن البُعد "الفئة" في التطبيق نوعَي الإصدار "مجاني" و "مدفوع"، ولكن لا تتضمّن التبعية سوى نوعَي الإصدار "تجريبي" و "مدفوع" للبُعد نفسه.

    يُرجى العِلم أنّه بالنسبة إلى سمة نوع إصدار معيّنة متوفّرة في كل من التطبيق والعناصر الاعتمادية للمكتبة، لن تحدث أي مشكلة عندما تتضمّن المكتبة نوع إصدار لا يتضمّنه تطبيقك. ويرجع ذلك إلى أنّ المكوّن الإضافي لا يطلب هذا النوع من التبعية.

    استخدِم matchingFallbacks لتحديد بدائل مطابقة لنوع المنتج "مجاني" في التطبيق، كما هو موضّح هنا:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
  • يتضمّن عنصر اعتمادية المكتبة سمة إصدار لا يتضمّنها تطبيقك.

    على سبيل المثال، يتضمّن عنصر اعتمادية المكتبة إصدارات متوافقة مع البُعد "minApi"، ولكن يتضمّن تطبيقك إصدارات متوافقة مع البُعد "tier" فقط. عندما تريد إنشاء الإصدار "freeDebug" من تطبيقك، لا تعرف الإضافة ما إذا كان يجب استخدام الإصدار "minApi23Debug" أو "minApi18Debug" من التبعية.

    يُرجى العِلم أنّه لا توجد مشكلة عندما يتضمّن تطبيقك سمة إصدار لا يتضمّنها عنصر اعتمادية المكتبة. ويرجع ذلك إلى أنّ المكوّن الإضافي يطابق إصدارات السمات المتوفّرة فقط في التبعية. على سبيل المثال، إذا لم تتضمّن إحدى التبعيات سمة خاصة بواجهات التطبيق الثنائية (ABI)، سيستخدم إصدار "freeX86Debug" من تطبيقك إصدار "freeDebug" من التبعية.

    استخدِم missingDimensionStrategy في الحظر defaultConfig لتحديد النكهة التلقائية التي سيختارها المكوّن الإضافي من كل سمة غير متوفّرة، كما هو موضّح في المثال التالي. يمكنك أيضًا إلغاء اختياراتك في القسم productFlavors، وبالتالي يمكن لكل صيغة تحديد استراتيجية مطابقة مختلفة لسمة غير متوفّرة.

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }

لمزيد من المعلومات، راجِع matchingFallbacks وmissingDimensionStrategy في مرجع DSL الخاص بـ "المكوّن الإضافي لنظام Gradle المتوافق مع Android".

ضبط إعدادات التوقيع

لا يوقّع Gradle حِزمة APK أو AAB لإصدارك إلا إذا حدّدت بشكل صريح إعدادات توقيع لهذا الإصدار. إذا لم يكن لديك مفتاح توقيع حتى الآن، يمكنك إنشاء مفتاح تحميل وملف تخزين مفاتيح باستخدام "استوديو Android".

لضبط إعدادات التوقيع يدويًا لنوع إصدارك باستخدام إعدادات الإصدار في Gradle، اتّبِع الخطوات التالية:

  1. أنشئ ملف تخزين مفاتيح. ملف تخزين المفاتيح هو ملف ثنائي يحتوي على مجموعة من المفاتيح الخاصة. يجب الاحتفاظ بملف تخزين المفاتيح في مكان آمن وموثوق به.
  2. أنشئ مفتاحًا خاصًا. يُستخدم المفتاح الخاص لتوقيع تطبيقك بغرض توزيعه، ولا يتم تضمينه مطلقًا مع التطبيق أو الإفصاح عنه لأي جهات خارجية غير مصرّح لها.
  3. أضِف إعدادات التوقيع إلى ملف build.gradle.kts على مستوى الوحدة:

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

ملاحظة: لا يُعدّ تضمين كلمات المرور الخاصة بمفتاح الإصدار وملف تخزين المفاتيح داخل ملف الإنشاء من ممارسات الأمان الجيدة. بدلاً من ذلك، اضبط ملف الإنشاء للحصول على هذه كلمات المرور من متغيّرات البيئة أو اطلب من عملية الإنشاء أن تطلب منك إدخال هذه كلمات المرور.

للحصول على كلمات المرور هذه من متغيّرات البيئة، اتّبِع الخطوات التالية:

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

بدلاً من ذلك، يمكنك تحميل ملف تخزين المفاتيح من ملف خصائص محلي. لأسباب تتعلّق بالأمان، لا تُضِف هذا الملف إلى نظام إدارة المصادر. بدلاً من ذلك، يمكنك إعدادها محليًا لكل مطوِّر. لمزيد من المعلومات، يُرجى الاطّلاع على إزالة معلومات التوقيع من ملفات الإصدار.

بعد إكمال هذه العملية، يمكنك توزيع تطبيقك ونشره على Google Play.

تحذير: احتفِظ بملف تخزين المفاتيح والمفتاح الخاص في مكان آمن وموثوق به، وتأكَّد من توفُّر نُسخ احتياطية آمنة منهما. في حال استخدام ميزة "توقيع التطبيق من Google Play" وفقدان مفتاح التحميل، يمكنك طلب إعادة ضبطه باستخدام Play Console. إذا كنت تنشر تطبيقًا بدون استخدام ميزة "توقيع التطبيق" من Play (للتطبيقات التي تم إنشاؤها قبل أغسطس 2021) وفقدت مفتاح توقيع التطبيق، لن تتمكّن من نشر أي تحديثات لتطبيقك، لأنّه يجب دائمًا توقيع جميع إصدارات تطبيقك باستخدام المفتاح نفسه.

توقيع تطبيقات Wear OS

عند نشر تطبيقات Wear OS، يجب توقيع كل من حزمة APK الخاصة بالساعة وحزمة APK الاختيارية الخاصة بالهاتف باستخدام المفتاح نفسه. لمزيد من المعلومات حول إنشاء حِزم تطبيقات Wear OS وتوقيعها، راجِع مقالة إنشاء حِزم تطبيقات Wear وتوزيعها.