كتابة مكوّنات Gradle الإضافية

"المكوّن الإضافي لنظام Gradle المتوافق مع Android" ‏ (AGP) هو نظام الإصدار الرسمي لتطبيقات Android. وتتضمّن دعمًا لتجميع العديد من أنواع المصادر المختلفة وربطها معًا في تطبيق يمكنك تشغيله على جهاز Android فعلي أو محاكي.

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

دورة حياة واجهة برمجة التطبيقات في AGP

يتّبع المكوّن الإضافي لنظام Gradle المتوافق مع Android مراحل نشاط ميزات Gradle لتحديد حالة واجهات برمجة التطبيقات الخاصة به:

  • داخلي: غير مخصّص للاستخدام العام
  • قيد التطوير: متاحة للاستخدام العام ولكنّها ليست نهائية، ما يعني أنّها قد لا تكون متوافقة مع الأنظمة القديمة في الإصدار النهائي
  • علنية: متاحة للاستخدام العام ومستقرة
  • متوقفة نهائيًا: لم تعُد متوافقة، وتم استبدالها بواجهات برمجة تطبيقات جديدة

سياسة الإيقاف النهائي

يتطوّر "مكوّن Android الإضافي لـ Gradle" من خلال إيقاف واجهات برمجة التطبيقات القديمة نهائيًا واستبدالها بواجهات برمجة تطبيقات جديدة ومستقرة ولغة جديدة خاصة بالنطاق (DSL). سيشمل هذا التطوّر إصدارات متعددة من AGP، ويمكنك معرفة المزيد عنه في المخطط الزمني لنقل بيانات AGP API/DSL.

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

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

التحويل البرمجي باستخدام عنصر واجهة برمجة تطبيقات AGP

لكتابة مكوّن إضافي مخصّص لنظام Gradle أو منطق تصميم (مثل دليل buildSrc أو وحدة مكوّن إضافي تقليدية أو مشروع مكوّن إضافي مستقل) يوسّع AGP، يمكنك التجميع باستخدام عنصر gradle-api في AGP بدلاً من عنصر تنفيذ المكوّن الإضافي الكامل.

لماذا يتم التجميع باستخدام العنصر gradle-api؟

ينشر "المكوّن الإضافي لنظام Gradle المتوافق مع Android" عنصرَين أساسيَّين:

  • com.android.tools.build:gradle-api: يحتوي على واجهات برمجة تطبيقات AGP الرسمية، بما في ذلك واجهات برمجة التطبيقات الثابتة (مثل AndroidComponentsExtension وVariant وواجهات DSL العامة) وواجهات برمجة التطبيقات قيد التطوير (المُشار إليها بـ @Incubating) وواجهات برمجة التطبيقات المتوقفة نهائيًا (المُشار إليها بـ @Deprecated).
  • ‫com.android.tools.build:gradle: يحتوي على عملية التنفيذ الكاملة لوقت تشغيل "المكوّن الإضافي لنظام Gradle المتوافق مع Android"، بما في ذلك الفئات الداخلية ومهام الإنشاء وعمليات دمج برامج التجميع.

يوفر التجميع مقابل عنصر gradle-api العديد من المزايا الرئيسية:

  • تجنُّب واجهات برمجة التطبيقات الداخلية: يضمن ذلك أن يصل رمز المكوّن الإضافي إلى واجهات برمجة التطبيقات الرسمية فقط. يمنع ذلك الاعتماد غير المقصود على فئات التنفيذ الداخلية (com.android.build.gradle.internal.*) التي يمكن تغييرها أو إزالتها في إصدارات AGP بدون تحذير.
  • عمليات إنشاء أسرع وبصمة أصغر: يحتوي العنصر gradle-api على شجرة تبعية أصغر بكثير من العنصر gradle الكامل. يؤدي ذلك إلى تقليل أحجام عمليات التنزيل وتجنُّب تلوّث مسار الفئة وتسريع عملية تجميع منطق الإصدار.
  • عزل بيئة التشغيل: لا تتطلّب المكوّنات الإضافية المخصّصة سوى تعريفات واجهة برمجة التطبيقات أثناء التجميع. أثناء التشغيل، يتم توفير تنفيذ AGP الكامل من خلال إصدار Android عند تطبيق المكوّن الإضافي لمكتبة أو تطبيق Android.

إضافة التبعية

أضِف العنصر gradle-api كاعتمادية في ملف الإصدار الخاص بمشروع المكوّن الإضافي أو دليل buildSrc. يتم نشر عنصر gradle-api في مستودع Maven من Google. لحلّ مشكلة العنصر، تأكَّد من أنّ إصدارك يتضمّن مستودع google(). على سبيل المثال، يمكنك ضبطه في مجموعة dependencyResolutionManagement.repositories لملف settings.gradle أو settings.gradle.kts أو في مجموعة repositories لملف إصدار المكوّن الإضافي أو ملف إصدار دليل buildSrc.

باستخدام قائمة إصدارات

إذا كان مشروعك يستخدم قائمة إصدارات، حدِّد اعتمادية gradle-api في ملف gradle/libs.versions.toml:

[versions]
androidGradlePlugin = "9.4.0"

[libraries]
android-gradle-plugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" }

ينشئ الاسم المستعار لكتالوج android-gradle-plugin-api دالة استرداد القيمة التي تمنع أخطاء الكتابة libs.android.gradle.plugin.api.

بعد ذلك، أضِف libs.android.gradle.plugin.api إلى قسم dependencies في ملف إصدار المكوّن الإضافي:

Kotlin

dependencies {
    compileOnly(libs.android.gradle.plugin.api)
}

أنيق

dependencies {
    compileOnly libs.android.gradle.plugin.api
}

بدون كتالوج الإصدارات

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

Kotlin

dependencies {
    compileOnly("com.android.tools.build:gradle-api:9.4.0")
}

أنيق

dependencies {
    compileOnly 'com.android.tools.build:gradle-api:9.4.0'
}

استخدِم خيار الضبط compileOnly كخيار تلقائي للمكوّنات الإضافية المستقلة المخصّصة للنشر. يطبّق المشروع المستهلك مكوّن AGP الإضافي ويوفّر فئات وقت التشغيل الخاصة به في مسار فئة buildscript. يؤدي استخدام إعدادات compileOnly إلى منع المكوّن الإضافي المنشور من تسريب تبعية وقت التشغيل في إصدار معيّن من AGP وتجنُّب تعارضات الإصدارات للمستهلكين. إذا كنت تطوّر منطق إصدار داخلي، مثل المكوّنات الإضافية في دليل buildSrc أو المكوّنات الإضافية التقليدية في الإصدارات المركّبة، استخدِم خيار الضبط implementation بدلاً من ذلك. يتم تشغيل هذه المكوّنات الإضافية ضمن عملية الإنشاء وتتطلّب استخدام "المكوّن الإضافي لنظام Gradle المتوافق مع Android" في مسار فئة وقت التشغيل.

أساسيات إنشاء Gradle

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

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

مسرد أنواع Gradle غير المباشرة

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

Provider<T>
توفّر قيمة من النوع T (حيث يشير "T" إلى أي نوع)، ويمكن قراءتها أثناء مرحلة التنفيذ باستخدام get() أو تحويلها إلى Provider<S> جديد (حيث يشير "S" إلى نوع آخر) باستخدام الطُرق map() وflatMap() وzip(). يُرجى العِلم أنّه يجب عدم استدعاء get() مطلقًا أثناء مرحلة الضبط.
  • ‫map(): تقبل دالة lambda وتُنتج Provider من النوع S، Provider<S>. تأخذ وسيطة lambda للدالة map() القيمة T وتنتج القيمة S. لا يتم تنفيذ دالة lambda على الفور، بل يتم تأجيل تنفيذها إلى حين استدعاء get() على Provider<S> الناتج، ما يجعل السلسلة بأكملها كسولة.
  • flatMap(): تقبل أيضًا دالة lambda وتنتج Provider<S>، ولكن دالة lambda تأخذ القيمة T وتنتج Provider<S> (بدلاً من إنتاج القيمة S مباشرةً). استخدِم flatMap()‎ عندما يتعذّر تحديد S في وقت الضبط ويمكنك الحصول على Provider<S> فقط. من الناحية العملية، إذا استخدمت map() وحصلت على نتيجة من النوع Provider<Provider<S>>، يعني ذلك على الأرجح أنّه كان عليك استخدام flatMap() بدلاً من ذلك.
  • zip(): تتيح لك هذه الدالة دمج مثيلَين من Provider لإنشاء مثيل جديد من Provider، مع قيمة يتم احتسابها باستخدام دالة تدمج القيم من مثيلَي Providers المُدخلَين.
Property<T>
تنفّذ Provider<T>، لذا توفّر أيضًا قيمة من النوع T. على عكس Provider<T>، الذي يكون للقراءة فقط، يمكنك أيضًا ضبط قيمة Property<T>. هناك طريقتان لإجراء ذلك:
  • يمكنك ضبط قيمة من النوع T مباشرةً عندما تكون متاحة، بدون الحاجة إلى عمليات حسابية مؤجَّلة.
  • اضبط Provider<T> آخر كمصدر لقيمة Property<T>. في هذه الحالة، يتم إنشاء القيمة T فقط عند استدعاء Property.get().
TaskProvider
Implements Provider<Task>. لإنشاء TaskProvider، استخدِم tasks.register() وليس tasks.create()، لضمان إنشاء مثيل للمهام بشكل كسول عند الحاجة إليها فقط. يمكنك استخدام flatMap() للوصول إلى مخرجات Task قبل إنشاء Task، ما قد يكون مفيدًا إذا أردت استخدام المخرجات كمدخلات لمثيلات Task أخرى.

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

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

في ما يلي مثال على تسجيل مهمتَين، GitVersionTask وManifestProducerTask، مع تأجيل إنشاء مثيلات Task إلى أن تصبح مطلوبة فعليًا. تم ضبط قيمة الإدخال ManifestProducerTask على Provider تم الحصول عليها من مخرجات GitVersionTask، لذا تعتمد ManifestProducerTask ضمنيًا على GitVersionTask.

// Register a task lazily to get its TaskProvider.
val gitVersionProvider: TaskProvider =
    project.tasks.register("gitVersionProvider", GitVersionTask::class.java) {
        it.gitVersionOutputFile.set(
            File(project.buildDir, "intermediates/gitVersionProvider/output")
        )
    }

...

/**
 * Register another task in the configuration block (also executed lazily,
 * only if the task is required).
 */
val manifestProducer =
    project.tasks.register(variant.name + "ManifestProducer", ManifestProducerTask::class.java) {
        /**
         * Connect this task's input (gitInfoFile) to the output of
         * gitVersionProvider.
         */
        it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
    }

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

على الرغم من أنّك ستكتب مهامًا مخصّصة تستهلك المدخلات و/أو تنتج المخرجات، لا يوفّر المكوّن الإضافي لنظام Gradle المتوافق مع Android إمكانية الوصول العام إلى مهامه مباشرةً. وهي تفاصيل تنفيذية تخضع للتغيير من إصدار إلى آخر. بدلاً من ذلك، يوفّر مكوّن AGP الإضافي واجهة Variant API وإمكانية الوصول إلى مخرجات مهامه أو ملفات الإصدار التي يمكنك قراءتها وتحويلها. يُرجى الاطّلاع على Variant API والعناصر والمهام في هذا المستند لمزيد من المعلومات.

مراحل إنشاء Gradle

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

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

مرحلة الإعداد

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

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

مرحلة التطبيق

في مرحلة التنفيذ، يتم تنفيذ المهام المطلوبة والمهام التي تعتمد عليها. على وجه التحديد، يتم تنفيذ طرق فئة Task التي تم وضع علامة @TaskAction عليها. أثناء تنفيذ المهمة، يُسمح لك بالقراءة من المدخلات (مثل الملفات) وحلّ مقدّمي الخدمات الكسولين من خلال استدعاء Provider<T>.get(). يؤدي حلّ مقدّمي الخدمات الكسولين بهذه الطريقة إلى بدء سلسلة من طلبات map() أو flatMap() التي تتبع معلومات تبعية المهام الواردة في مقدّم الخدمة. يتم تشغيل المهام بشكل كسول لتجسيد القيم المطلوبة.

واجهة برمجة التطبيقات Variant API والقطع الأثرية والمهام

‫Variant API هي آلية إضافة في المكوّن الإضافي لنظام Gradle المتوافق مع Android تتيح لك معالجة الخيارات المختلفة التي تؤثر في إصدار Android، والتي يتم ضبطها عادةً باستخدام DSL في ملفات إعدادات الإصدار. تتيح لك Variant API أيضًا الوصول إلى العناصر الوسيطة والنهائية التي يتم إنشاؤها من خلال عملية الإنشاء، مثل ملفات الفئات أو ملفات البيان المدمجة أو ملفات APK/AAB.

سير عملية إنشاء تطبيقات Android ونقاط الإضافة

عند التفاعل مع &quot;المكوّن الإضافي لنظام Gradle المتوافق مع Android&quot;، استخدِم نقاط الإضافة المصمَّمة خصيصًا بدلاً من تسجيل عمليات الاستدعاء المعتادة في إحدى مراحل نشاط Gradle (مثل afterEvaluate()) أو إعداد تبعيات Task صريحة. تُعتبر المهام التي ينشئها مكوّن AGP تفاصيل تنفيذية ولا يتم عرضها كواجهة برمجة تطبيقات عامة. يجب تجنُّب محاولة الحصول على نُسخ من عناصر Task أو تخمين أسماء Task وإضافة عمليات استدعاء أو تبعيات إلى عناصر Task هذه مباشرةً.

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

  1. تحليل DSL: يتم تقييم نصوص تصميم البرامج، وإنشاء وضبط الخصائص المختلفة لعناصر Android DSL من مجموعة android. يتم أيضًا تسجيل عمليات استدعاء Variant API الموضّحة في الأقسام التالية خلال هذه المرحلة.
  2. finalizeDsl(): هي دالة رد اتصال تتيح لك تغيير عناصر DSL قبل قفلها لإنشاء المكوّنات (الخيارات). يتم إنشاء كائنات VariantBuilder استنادًا إلى البيانات الواردة في كائنات DSL.

  3. قفل DSL: تم قفل DSL الآن ولم يعُد من الممكن إجراء تغييرات.

  4. ‫beforeVariants(): يمكن أن تؤثر دالة الاستدعاء هذه في المكوّنات التي يتم إنشاؤها وبعض خصائصها، من خلال VariantBuilder. ولا يزال يسمح بإجراء تعديلات على مسار عملية الإنشاء والنواتج التي يتم إنتاجها.

  5. إنشاء النُسخ: تم الانتهاء من تحديد قائمة المكوّنات والملفات التي سيتم إنشاؤها ولا يمكن تغييرها.

  6. onVariants(): في ردّ الاتصال هذا، يمكنك الوصول إلى عناصر Variant التي تم إنشاؤها ويمكنك ضبط القيم أو مقدّمي الخدمات لقيم Property التي تحتوي عليها، ليتم احتسابها بشكل كسول.

  7. قفل النُسخ المتنوعة: يتم الآن قفل عناصر النُسخ المتنوعة ولا يمكن إجراء أي تغييرات عليها.

  8. المهام التي تم إنشاؤها: يتم استخدام عناصر Variant وقيمها Property لإنشاء مثيلات Task اللازمة لإجراء عملية الإنشاء.

يقدّم AGP AndroidComponentsExtension يتيح لك تسجيل عمليات ردّ الاتصال لـ finalizeDsl() وbeforeVariants() وonVariants(). تتوفّر الإضافة في نصوص الإنشاء البرمجية من خلال مجموعة androidComponents:

// This is used only for configuring the Android build through DSL.
android { ... }

// The androidComponents block is separate from the DSL.
androidComponents {
   finalizeDsl { extension ->
      ...
   }
}

ومع ذلك، ننصحك بالاحتفاظ بنصوص الإنشاء البرمجية فقط للإعدادات التعريفية باستخدام لغة DSL الخاصة بكتلة android ونقل أي منطق إلزامي مخصّص إلى دليل buildSrc أو المكوّنات الإضافية الخارجية. يمكنك أيضًا الاطّلاع على نماذج buildSrc في مستودع GitHub الخاص بوصفات Gradle لمعرفة كيفية إنشاء مكوّن إضافي في مشروعك. تأكَّد من أنّ المكوّن الإضافي يتم تجميعه مع العنصر gradle-api في ملف الإصدار. في ما يلي مثال على تسجيل عمليات الاستدعاء من رمز المكوّن الإضافي:

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            ...
        }
    }
}

لنلقِ نظرة عن كثب على عمليات الاستدعاء المتاحة ونوع حالات الاستخدام التي يمكن أن تتيحها الإضافة في كل منها:

finalizeDsl(callback: (DslExtensionT) -> Unit)

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

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            extension.buildTypes.create("extra").let {
                it.isJniDebuggable = true
            }
        }
    }
}

beforeVariants()

في هذه المرحلة من عملية الإنشاء، يمكنك الوصول إلى عناصر VariantBuilder التي تحدّد النُسخ التي سيتم إنشاؤها وخصائصها. على سبيل المثال، يمكنك إيقاف صيغ معيّنة أو اختباراتها آليًا أو تغيير قيمة سمة (مثل minSdk) لصيغة محدّدة فقط. على غرار السمة finalizeDsl()، يجب أن يتم حلّ جميع القيم التي تقدّمها في وقت الضبط وألا تعتمد على مدخلات خارجية. يجب عدم تعديل عناصر VariantBuilder بعد انتهاء تنفيذ ردّ الاتصال beforeVariants().

androidComponents {
    beforeVariants { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

تتلقّى دالة الاستدعاء beforeVariants() بشكل اختياري VariantSelector، ويمكنك الحصول عليها من خلال طريقة selector() في androidComponentsExtension. يمكنك استخدامها لفلترة المكوّنات المشارِكة في استدعاء دالة الاستدعاء استنادًا إلى اسمها أو نوع الإصدار أو نكهة المنتج.

androidComponents {
    beforeVariants(selector().withName("adfree")) { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

onVariants()

عند استدعاء onVariants()، تكون جميع العناصر التي سيتم إنشاؤها بواسطة AGP قد تم تحديدها، لذا لن تتمكن من إيقافها بعد ذلك. ومع ذلك، يمكنك تعديل بعض القيم المستخدَمة للمهام من خلال ضبطها لسمات Property في عناصر Variant. بما أنّ قيم Property لن يتم حلّها إلا عند تنفيذ مهام "المكوّن الإضافي لنظام Gradle المتوافق مع Android"، يمكنك ربطها بأمان بموفّري الخدمات من المهام المخصّصة التي ستنفّذ أي عمليات حسابية مطلوبة، بما في ذلك القراءة من الإدخالات الخارجية، مثل الملفات أو الشبكة.

// onVariants also supports VariantSelectors:
onVariants(selector().withBuildType("release")) { variant ->
    // Gather the output when we are in single mode (no multi-apk).
    val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

    // Create version code generating task
    val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
        it.outputFile.set(project.layout.buildDirectory.file("${variant.name}/versionCode.txt"))
    }
    /**
     * Wire version code from the task output.
     * map() will create a lazy provider that:
     * 1. Runs just before the consumer(s), ensuring that the producer
     * (VersionCodeTask) has run and therefore the file is created.
     * 2. Contains task dependency information so that the consumer(s) run after
     * the producer.
     */
    mainOutput.versionCode.set(versionCodeTask.map { it.outputFile.get().asFile.readText().toInt() })
}

المساهمة بالمصادر التي تم إنشاؤها في عملية الإنشاء

يمكن أن يساهم المكوّن الإضافي في إنشاء بضعة أنواع من المصادر، مثل:

للاطّلاع على القائمة الكاملة بالمصادر التي يمكنك إضافتها، يُرجى مراجعة Sources API.

يوضّح مقتطف الرمز هذا كيفية إضافة مجلد مصدر مخصّص باسم ${variant.name} إلى مجموعة رموز المصدر في Java باستخدام الدالة addStaticSourceDirectory(). بعد ذلك، تعالج سلسلة أدوات Android هذا المجلد.

onVariants { variant ->
    variant.sources.java?.let { java ->
        java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
    }
}

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

يعرض مقتطف الرمز هذا كيفية إضافة دليل يتضمّن موارد Android تم إنشاؤها من مهمة مخصّصة إلى مجموعة رموز المصدر res. تتشابه العملية مع أنواع المصادر الأخرى.

onVariants(selector().withBuildType("release")) { variant ->
    // Step 1. Register the task.
    val resCreationTask =
       project.tasks.register<ResCreatorTask>("create${variant.name}Res")

    // Step 2. Register the task output to the variant-generated source directory.
    variant.sources.res?.addGeneratedSourceDirectory(
       resCreationTask,
       ResCreatorTask::outputDirectory)
    }

...

// Step 3. Define the task.
abstract class ResCreatorTask: DefaultTask() {
   @get:OutputFiles
   abstract val outputDirectory: DirectoryProperty

   @TaskAction
   fun taskAction() {
      // Step 4. Generate your resources.
      ...
   }
}

لمزيد من التفاصيل، يمكنك الاطّلاع على وصفة addCustomAsset.

الوصول إلى العناصر وتعديلها

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

يمكنك العثور على قائمة بالقطع الأثرية المتوافقة حاليًا في المستندات المرجعية لفئة Artifact. لكل نوع من أنواع العناصر خصائص معيّنة من المفيد معرفتها:

عدد العناصر في الحقل

يمثّل عدد العناصر في الحقل Artifact عدد مثيلات FileSystemLocation، أو عدد الملفات أو الأدلة من نوع العنصر. يمكنك الحصول على معلومات حول عدد العناصر في الحقل من خلال التحقّق من الفئة الرئيسية: ستكون العناصر التي تحتوي على FileSystemLocation واحد فئة فرعية من Artifact.Single، وستكون العناصر التي تحتوي على نُسخ متعددة من FileSystemLocation فئة فرعية من Artifact.Multiple.

نوع واحد FileSystemLocation

يمكنك التحقّق مما إذا كان Artifact يمثّل ملفات أو أدلة من خلال الاطّلاع على نوع FileSystemLocation الذي تم تحديد مَعلماته، والذي يمكن أن يكون إما RegularFile أو Directory.

العمليات المتوافقة

يمكن لكل فئة Artifact تنفيذ أي من الواجهات التالية للإشارة إلى العمليات التي تتيحها:

  • Transformable: تتيح استخدام Artifact كإدخال في Task تُجري عمليات تحويل عشوائية عليه وتُخرج نسخة جديدة من Artifact.
  • ‫Appendable: تنطبق فقط على العناصر التي تُعدّ فئات فرعية من Artifact.Multiple. ويعني ذلك أنّه يمكن إلحاق Artifact، أي أنّ Task المخصّص يمكنه إنشاء نُسخ جديدة من نوع Artifact هذا ستتم إضافتها إلى القائمة الحالية.
  • ‫Replaceable: تنطبق فقط على العناصر التي تُعدّ فئات فرعية من Artifact.Single. يمكن استبدال Artifact قابل للاستبدال بنسخة جديدة تمامًا، يتم إنتاجها كمخرجات من Task.

بالإضافة إلى العمليات الثلاث لتعديل العناصر، يتيح كل عنصر إجراء عملية get() (أو getAll()) التي تعرض Provider مع الإصدار النهائي من العنصر (بعد الانتهاء من جميع العمليات عليه).

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

نقطة الدخول إلى عمليات التسجيل هي فئة Artifacts. يوضّح مقتطف الرمز التالي كيفية الوصول إلى مثيل من Artifacts من سمة في عنصر Variant في دالة الاستدعاء onVariants().

يمكنك بعد ذلك تمرير TaskProvider المخصّص للحصول على عنصر TaskBasedOperation (1)، واستخدامه لربط مدخلاته ومخرجاته باستخدام إحدى طرق wiredWith* (2).

تعتمد الطريقة الدقيقة التي تحتاج إلى اختيارها على عدد العناصر في الحقل ونوع FileSystemLocation الذي يتم تنفيذه بواسطة Artifact الذي تريد تحويله.

وأخيرًا، يمكنك تمرير نوع Artifact إلى طريقة تمثّل العملية التي تم اختيارها على عنصر *OperationRequest الذي تحصل عليه في المقابل، على سبيل المثال، toAppendTo() أو toTransform() أو toCreate() (3).

androidComponents.onVariants { variant ->
    val manifestUpdater = // Custom task that will be used for the transform.
            project.tasks.register(variant.name + "ManifestUpdater", ManifestTransformerTask::class.java) {
                it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
            }
    // (1) Register the TaskProvider w.
    val variant.artifacts.use(manifestUpdater)
         // (2) Connect the input and output files.
        .wiredWithFiles(
            ManifestTransformerTask::mergedManifest,
            ManifestTransformerTask::updatedManifest)
        // (3) Indicate the artifact and operation type.
        .toTransform(SingleArtifact.MERGED_MANIFEST)
}

في هذا المثال، MERGED_MANIFEST هو SingleArtifact، وهو RegularFile. لهذا السبب، نحتاج إلى استخدام طريقة wiredWithFiles التي تقبل مرجع RegularFileProperty واحدًا للإدخال، وRegularFileProperty واحدًا للإخراج. هناك طرق wiredWith* أخرى في فئة TaskBasedOperation ستعمل مع مجموعات أخرى من عدد العناصر في الحقل Artifact وأنواع FileSystemLocation.

لمزيد من المعلومات حول توسيع نطاق &quot;المكوّن الإضافي لنظام Gradle المتوافق مع Android&quot;، ننصحك بقراءة الأقسام التالية من دليل نظام التصميم المستند إلى Gradle: