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

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

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

مراحل نشاط واجهة برمجة تطبيقات AGP

يتّبع AGP دورة حياة ميزة Gradle لتحديد حالة واجهات برمجة التطبيقات الخاصة به:

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

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

يتطوّر AGP بالتزامن مع إيقاف واجهات برمجة التطبيقات القديمة واستبدالها بواجهات برمجة تطبيقات جديدة وثابتة ولغة جديدة خاصة بالنطاق (DSL). ويشمل هذا التطوّر إصدارات متعدّدة من AGP، ويمكنك الاطّلاع على المزيد من المعلومات حوله على المخطط الزمني لنقل بيانات AGP API/DSL.

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

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

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

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

نفترض معرفة أساسية حول كيفية عمل Gradle، بما في ذلك كيفية تهيئة المشروعات وتعديل ملفات الإصدار وتطبيق المكونات الإضافية وتشغيل المهام. للتعرّف على أساسيات Gradle في ما يتعلق بـ AGP، ننصحك بمراجعة ضبط البنية. للاطّلاع على إطار العمل العام لتخصيص مكوّنات 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 مباشرةً). استخدِم دالة Fla Map() عندما يتعذّر تحديد 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
ينفِّذ 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 ببعض المهام الأخرى وأصبحت قيمتها مطلوبة.

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

مراحل إنشاء Gradle

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

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

مرحلة الضبط

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

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

مرحلة التنفيذ

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

"واجهة برمجة التطبيقات" و"الأدوات" و"المهام" للنسخة التجريبية

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

نقاط تدفق إصدار Android والإضافات

عند التفاعل مع AGP، استخدِم نقاط إضافة تم إنشاؤها خصيصًا بدلاً من تسجيل استدعاءات دورة حياة 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 النماذج في مستودع Gradle المخصص لوصفات الطعام على GitHub للتعرّف على كيفية إنشاء مكوّن إضافي في مشروعك. في ما يلي مثال على تسجيل عمليات معاودة الاتصال من رمز المكوّن الإضافي:

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 لن يتم حلها إلا عند تنفيذ مهام AGP، يمكنك إرسالها بأمان إلى المزوّدين من مهامك المخصَّصة التي ستُجري أي عمليات حوسبة مطلوبة، بما في ذلك القراءة من مدخلات خارجية مثل الملفات أو الشبكة.

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

نقطة الدخول إلى عمليات التسجيل هي فئة 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.

لمعرفة المزيد حول تمديد AGP، ننصحك بقراءة الأقسام التالية من دليل نظام تصميم Gradle: