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

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

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

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

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

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

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

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

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

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

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

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

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

مسرد مصطلحات الأنواع الكسولة في Gradle

يوفّر Gradle عددًا من الأنواع التي تعمل "كسول" أو تساعد في تأجيل العمليات الحسابية الثقيلة أو إنشاء Task إلى المراحل اللاحقة من التصميم. تشكّل هذه الأنواع جوهر العديد من واجهات برمجة التطبيقات في IDE و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 مباشرةً). استخدِم FlaMap() عندما لا يمكن تحديد 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 والعناصر والمهام

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

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

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

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

  1. تحليل لغة وصف البيانات: يتم ذلك عند تقييم نصوص البناء، وعند إنشاء والضبط الخاصين بالخصائص المختلفة لعناصر لغة وصف البيانات في Android من العنصر 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 للتعرّف على كيفية إنشاء مكوّن إضافي في مشروعك. في ما يلي مثال على تسجيل طلبات إعادة الاتصال من رمز المكوّن الإضافي:

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 في دالة callback الخاصة ب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: