پلاگین های Gradle را بنویسید

پلاگین Android Gradle (AGP) سیستم ساخت رسمی برنامه های اندروید است. این شامل پشتیبانی از کامپایل انواع مختلف منابع و پیوند آنها با یکدیگر در یک برنامه است که می توانید آن را در یک دستگاه اندروید فیزیکی یا شبیه ساز اجرا کنید.

AGP حاوی نقاط توسعه برای پلاگین ها برای کنترل ورودی های ساخت و گسترش عملکرد آن از طریق مراحل جدید است که می تواند با وظایف ساخت استاندارد ادغام شود. نسخه‌های قبلی AGP دارای APIهای رسمی جدا از پیاده‌سازی داخلی نبودند. با شروع نسخه 7.0، AGP مجموعه‌ای از APIهای رسمی و پایدار دارد که می‌توانید به آنها اعتماد کنید.

چرخه عمر AGP API

AGP از چرخه عمر ویژگی Gradle پیروی می کند تا وضعیت API های خود را مشخص کند:

  • داخلی : برای استفاده عمومی در نظر گرفته نشده است
  • جوجه کشی : برای استفاده عمومی در دسترس است اما نهایی نیست، به این معنی که ممکن است در نسخه نهایی سازگار با عقب نباشند
  • عمومی : برای استفاده عمومی و پایدار موجود است
  • منسوخ شده : دیگر پشتیبانی نمی شود و با API های جدید جایگزین شده است

سیاست استهلاک

AGP با از بین رفتن APIهای قدیمی و جایگزینی آنها با APIهای جدید و پایدار و یک زبان خاص دامنه جدید (DSL) در حال تکامل است. این تکامل چندین نسخه AGP را در بر می گیرد و می توانید در جدول زمانی مهاجرت AGP API/DSL درباره آن اطلاعات بیشتری کسب کنید.

هنگامی که APIهای AGP منسوخ می شوند، برای این انتقال یا موارد دیگر، در نسخه اصلی فعلی همچنان در دسترس خواهند بود اما هشدارهایی را ایجاد می کنند. APIهای منسوخ شده به طور کامل از AGP در نسخه اصلی بعدی حذف خواهند شد. به عنوان مثال، اگر یک API در AGP 7.0 منسوخ شده باشد، در آن نسخه در دسترس خواهد بود و اخطار ایجاد می کند. آن API دیگر در AGP 8.0 در دسترس نخواهد بود.

برای دیدن نمونه‌هایی از APIهای جدید مورد استفاده در سفارشی‌سازی‌های ساخت رایج، به دستور العمل‌های پلاگین Android Gradle نگاهی بیندازید. آنها نمونه هایی از سفارشی سازی های رایج ساخت را ارائه می دهند. همچنین می‌توانید جزئیات بیشتری درباره APIهای جدید در اسناد مرجع ما بیابید.

اصول ساخت Gradle

این راهنما کل سیستم ساخت Gradle را پوشش نمی دهد. با این حال، حداقل مجموعه مفاهیم ضروری را برای کمک به شما در ادغام با API های ما پوشش می دهد و برای مطالعه بیشتر به اسناد اصلی Gradle پیوند می دهد.

ما دانش اولیه در مورد نحوه عملکرد Gradle، از جمله نحوه پیکربندی پروژه‌ها، ویرایش فایل‌های ساخت، اعمال افزونه‌ها و اجرای وظایف را فرض می‌کنیم. برای آشنایی با اصول اولیه Gradle در رابطه با AGP، توصیه می کنیم Configure your build را مرور کنید. برای آشنایی با چارچوب کلی برای سفارشی‌سازی پلاگین‌های Gradle، به توسعه پلاگین‌های سفارشی Gradle مراجعه کنید.

واژه نامه انواع تنبل Gradle

Gradle انواع مختلفی را ارائه می دهد که "تنبل" رفتار می کنند یا به تعویق محاسبات سنگین یا ایجاد Task به مراحل بعدی ساخت کمک می کنند. این انواع در هسته بسیاری از APIهای Gradle و AGP قرار دارند. لیست زیر شامل انواع اصلی Gradle است که در اجرای تنبل دخیل هستند و روش های کلیدی آنها.

Provider<T>
مقداری از نوع T را ارائه می دهد (که در آن "T" به معنای هر نوع است)، که می تواند در طول مرحله اجرا با استفاده از get() خوانده شود یا با استفاده از map() به یک Provider<S> (که در آن "S" به معنای نوع دیگری است) تبدیل شود. متدهای map() ، flatMap() و zip() . توجه داشته باشید که get() هرگز نباید در مرحله پیکربندی فراخوانی شود.
  • map() : یک لامبدا را می پذیرد و یک Provider از نوع S ، Provider<S> تولید می کند. آرگومان لامبدا برای map() مقدار T را می گیرد و مقدار S تولید می کند. لامبدا بلافاصله اجرا نمی شود. در عوض، اجرای آن به لحظه ای موکول می شود get() در Provider<S> فراخوانی می شود و کل زنجیره را تنبل می کند.
  • flatMap() : همچنین لامبدا را می پذیرد و Provider<S> را تولید می کند، اما lambda مقدار T را می گیرد و Provider<S> را تولید می کند (به جای اینکه مقدار S مستقیماً تولید کند). زمانی که S را نمی توان در زمان پیکربندی تعیین کرد و می توانید فقط Provider<S> دریافت کنید، از flatMap () استفاده کنید. در عمل، اگر 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 به کار دیگری متصل شده باشد و مقدار آن مورد نیاز باشد.

در حالی که کارهای سفارشی را می نویسید که ورودی ها را مصرف می کنند و/یا خروجی تولید می کنند، AGP دسترسی عمومی به وظایف خود را مستقیماً ارائه نمی دهد. آنها جزئیات پیاده سازی هستند که از نسخه ای به نسخه دیگر تغییر می کنند. در عوض، AGP Variant API و دسترسی به خروجی وظایف خود یا ساخت مصنوعات را ارائه می‌کند که می‌توانید آنها را بخوانید و تبدیل کنید. برای اطلاعات بیشتر ، Variant API، Artifacts و Tasks را در این سند ببینید.

مراحل ساخت گرید

ساختن یک پروژه ذاتاً فرآیندی پیچیده و نیازمند منابع است و ویژگی‌های مختلفی مانند اجتناب از پیکربندی کار، بررسی‌های به‌روز، و ویژگی ذخیره‌سازی پیکربندی وجود دارد که به به حداقل رساندن زمان صرف شده برای محاسبات تکرارپذیر یا غیر ضروری کمک می‌کند.

برای اعمال برخی از این بهینه‌سازی‌ها، اسکریپت‌ها و افزونه‌های Gradle باید در طول هر یک از مراحل ساخت Gradle متمایز از قوانین سخت‌گیرانه پیروی کنند: مقداردهی اولیه، پیکربندی و اجرا. در این راهنما، ما بر روی مراحل پیکربندی و اجرا تمرکز خواهیم کرد. شما می توانید اطلاعات بیشتری در مورد تمام مراحل در راهنمای چرخه عمر ساخت Gradle بیابید.

مرحله پیکربندی

در مرحله پیکربندی، اسکریپت‌های ساخت برای همه پروژه‌هایی که بخشی از ساخت هستند، ارزیابی می‌شوند، افزونه‌ها اعمال می‌شوند و وابستگی‌های ساخت برطرف می‌شوند. این مرحله باید برای پیکربندی بیلد با استفاده از اشیاء DSL و برای ثبت وظایف و ورودی های آنها به صورت تنبل استفاده شود.

از آنجایی که فاز پیکربندی همیشه اجرا می‌شود، صرف نظر از اینکه کدام وظیفه اجرا می‌شود، بسیار مهم است که آن را ناب نگه دارید و محاسبات را به ورودی‌هایی غیر از خود اسکریپت‌های ساخت محدود کنید. یعنی نباید برنامه های خارجی را اجرا کنید یا از شبکه بخوانید یا محاسبات طولانی را انجام دهید که می توانند به عنوان نمونه های Task مناسب به مرحله اجرا موکول شوند.

مرحله اجرا

در مرحله اجرا، وظایف درخواستی و وظایف وابسته به آنها اجرا می شود. به طور خاص، متد(های) کلاس Task که با @TaskAction مشخص شده اند اجرا می شوند. در طول اجرای کار، به شما اجازه داده می شود که از ورودی ها (مانند فایل ها) بخوانید و با فراخوانی Provider<T>.get() ارائه دهندگان تنبل را حل کنید. حل کردن ارائه‌دهنده‌های تنبل به این روش، دنباله‌ای از فراخوانی‌های map() یا flatMap() را آغاز می‌کند که اطلاعات وابستگی وظیفه موجود در ارائه‌دهنده را دنبال می‌کنند. وظایف با تنبلی اجرا می شوند تا ارزش های مورد نیاز تحقق یابد.

نوع API، مصنوعات و وظایف

Variant API یک مکانیسم برنامه افزودنی در افزونه Android Gradle است که به شما امکان می دهد گزینه های مختلف را که معمولاً با استفاده از DSL در فایل های پیکربندی ساخت تنظیم می شوند، دستکاری کنید و بر ساخت اندروید تأثیر بگذارند. Variant API همچنین به شما امکان دسترسی به مصنوعات میانی و نهایی را می دهد که توسط بیلد ایجاد می شوند، مانند فایل های کلاس، مانیفست ادغام شده یا فایل های APK/AAB.

نقاط جریان ساخت و توسعه اندروید

هنگام تعامل با AGP، به جای ثبت تماس‌های معمولی چرخه عمر Gradle (مانند afterEvaluate() ) یا تنظیم وابستگی‌های صریح Task ، از نقاط توسعه ویژه ساخته شده استفاده کنید. وظایف ایجاد شده توسط AGP جزییات پیاده سازی در نظر گرفته می شوند و به عنوان یک API عمومی در معرض نمایش قرار نمی گیرند. شما باید از تلاش برای دریافت نمونه هایی از اشیاء Task یا حدس زدن نام Task و افزودن تماس یا وابستگی مستقیم به آن اشیاء Task اجتناب کنید.

AGP مراحل زیر را برای ایجاد و اجرای Task Instance های خود تکمیل می کند که به نوبه خود مصنوعات ساخت را تولید می کند. مراحل اصلی درگیر در ایجاد شیء Variant با فراخوانی دنبال می‌شوند که به شما امکان می‌دهند در برخی از اشیاء ایجاد شده به عنوان بخشی از یک ساخت تغییرات ایجاد کنید. توجه به این نکته مهم است که همه فراخوان‌ها در مرحله پیکربندی (توضیح داده شده در این صفحه) اتفاق می‌افتند و باید سریع اجرا شوند و به جای آن، هر کار پیچیده را به نمونه‌های Task مناسب در مرحله اجرا موکول می‌کند.

  1. تجزیه DSL : این زمانی است که اسکریپت های ساخت ارزیابی می شوند، و زمانی که ویژگی های مختلف اشیاء DSL Android از بلوک android ایجاد و تنظیم می شوند. تماس‌های Variant API که در بخش‌های زیر توضیح داده شده‌اند نیز در این مرحله ثبت می‌شوند.
  2. finalizeDsl() : Callback که به شما امکان می دهد اشیاء DSL را قبل از قفل شدن برای ایجاد کامپوننت (نوع) تغییر دهید. اشیاء VariantBuilder بر اساس داده های موجود در اشیاء DSL ایجاد می شوند.

  3. قفل DSL : اکنون DSL قفل شده است و تغییرات دیگر امکان پذیر نیست.

  4. beforeVariants() : این callback می تواند از طریق VariantBuilder بر روی کامپوننت ها و برخی از ویژگی های آنها تأثیر بگذارد. همچنان اجازه می دهد تا تغییراتی در جریان ساخت و مصنوعات تولید شده ایجاد شود.

  5. ایجاد نوع : لیست اجزا و مصنوعاتی که ایجاد خواهند شد اکنون نهایی شده است و قابل تغییر نیست.

  6. onVariants() : در این callback، شما به اشیاء Variant ایجاد شده دسترسی پیدا می‌کنید و می‌توانید مقادیر یا ارائه‌دهنده‌هایی را برای مقادیر Property که حاوی آن‌ها هستند تنظیم کنید تا به صورت تنبلی محاسبه شوند.

  7. قفل متغیر : اکنون اشیاء مختلف قفل شده اند و تغییرات دیگر امکان پذیر نیست.

  8. Tasks ایجاد شده : اشیاء 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 بلوک اندروید نگه دارید و هر منطق ضروری سفارشی را به buildSrc یا افزونه‌های خارجی منتقل کنید . همچنین می‌توانید نگاهی به نمونه‌های buildSrc در مخزن Gradle recipes 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)

در این callback شما می توانید به اشیاء 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 نباید پس از اتمام اجرای callback 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() })
}

منابع تولید شده را در ساخت مشارکت دهید

افزونه شما می‌تواند چندین نوع منبع تولید شده را به اشتراک بگذارد، مانند:

برای فهرست کامل منابعی که می‌توانید اضافه کنید، به منابع API مراجعه کنید.

این قطعه کد نشان می دهد که چگونه می توان یک پوشه منبع سفارشی به نام ${variant.name} با استفاده از تابع addStaticSourceDirectory() به مجموعه منبع جاوا اضافه کرد. سپس زنجیره ابزار اندروید این پوشه را پردازش می کند.

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 را ببینید.

دستیابی و اصلاح مصنوعات

AGP علاوه بر اینکه به شما امکان می‌دهد ویژگی‌های ساده روی اشیاء Variant را تغییر دهید، یک مکانیسم توسعه نیز دارد که به شما امکان می‌دهد مصنوعات میانی و نهایی تولید شده در طول ساخت را بخوانید یا تبدیل کنید. برای مثال، می‌توانید محتوای فایل AndroidManifest.xml نهایی و ادغام‌شده را در یک Task سفارشی بخوانید تا آن را تجزیه و تحلیل کنید، یا می‌توانید محتوای آن را به طور کامل با فایل مانیفست تولید شده توسط Task سفارشی خود جایگزین کنید.

می‌توانید فهرست مصنوع‌هایی را که در حال حاضر پشتیبانی می‌شوند در مستندات مرجع کلاس Artifact پیدا کنید. هر نوع مصنوع دارای خواص خاصی است که دانستن آنها مفید است:

کاردینالیته

کاردینالیته یک Artifact تعداد نمونه های FileSystemLocation یا تعداد فایل ها یا دایرکتوری های نوع مصنوع را نشان می دهد. می‌توانید با بررسی کلاس اصلی یک مصنوع اطلاعاتی در مورد اصلی بودن آن به دست آورید: مصنوعات با یک FileSystemLocation یک زیر کلاس از Artifact.Single خواهند بود. مصنوعات با چندین نمونه FileSystemLocation یک زیر کلاس از Artifact.Multiple خواهند بود.

نوع FileSystemLocation

می‌توانید با نگاه کردن به نوع FileSystemLocation که می‌تواند یک RegularFile یا یک Directory باشد، بررسی کنید که آیا Artifact فایل‌ها یا دایرکتوری‌ها را نشان می‌دهد.

عملیات پشتیبانی شده

هر کلاس Artifact می‌تواند هر یک از اینترفیس‌های زیر را برای نشان دادن عملیاتی که پشتیبانی می‌کند پیاده‌سازی کند:

  • Transformable : به یک Artifact اجازه می دهد تا به عنوان ورودی برای یک Task استفاده شود که تغییرات دلخواه را روی آن انجام می دهد و نسخه جدیدی از Artifact را خروجی می دهد.
  • Appendable : فقط برای مصنوعاتی اعمال می شود که زیر کلاس Artifact.Multiple هستند. به این معنی که Artifact می تواند به آن اضافه شود، یعنی یک Task سفارشی می تواند نمونه های جدیدی از این نوع Artifact ایجاد کند که به لیست موجود اضافه می شود.
  • Replaceable : فقط برای مصنوعاتی اعمال می شود که زیر کلاس های Artifact.Single هستند. یک Artifact قابل تعویض را می توان با یک نمونه کاملاً جدید جایگزین کرد که به عنوان خروجی یک Task تولید می شود.

علاوه بر سه عملیات اصلاح کننده مصنوع، هر مصنوع از یک عملیات get() (یا getAll() ) پشتیبانی می کند که یک Provider با نسخه نهایی مصنوع برمی گرداند (پس از اتمام تمام عملیات روی آن).

پلاگین‌های متعدد می‌توانند هر تعداد عملیات روی آرتیفکت‌ها را از طریق callback 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 cardinality و FileSystemLocation کار می‌کند.

برای کسب اطلاعات بیشتر در مورد گسترش AGP، توصیه می کنیم بخش های زیر را از کتابچه راهنمای سیستم ساخت Gradle بخوانید: