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

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

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

مراحل نشاط واجهة برمجة التطبيقات في "مكوّن Android الإضافي في Gradle"

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

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

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

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

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

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

مراحل إنشاء Gradle

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

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

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

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

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

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

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

Variant API والعناصر والمهام

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

مسار إصدار Android ونقاط التوسيع

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

تُكمل "إضافة Android Gradle" الخطوات التالية لإنشاء وتنفيذ مثيلاتها من 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 اللازمة لتنفيذ عملية الإنشاء.

توفّر "مكوّنات Android الإنشائية" 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 لمعرفة كيفية إنشاء مكوّن إضافي في مشروعك. في ما يلي مثال على تسجيل عمليات الرجوع من رمز البرنامج المساعد:

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

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

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