كتابة مكوّنات 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، ستظل متاحة في هذا الإصدار وستُنشئ تحذيرات. لن تكون واجهة برمجة التطبيقات هذه متاحة في IDE AGP 8.0.

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

مراحل إنشاء Gradle

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

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

مرحلة الضبط

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

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

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

في مرحلة التنفيذ، تتم تنفيذ المهام المطلوبة والمهام التابعة لها. على وجه التحديد، يتم تنفيذ methods فئة 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 هذه مباشرةً.

يُكمل "أداة تطوير التطبيقات" الخطوات التالية لإنشاء نُسخ 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 في استدعاء 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: