כתיבת יישומי פלאגין של Gradle

הפלאגין של Android Gradle (AGP) הוא מערכת ה-build הרשמית של Android תרגום מכונה. הוא כולל תמיכה בהידור של סוגים רבים ושונים של מקורות ולקשר אותם לאפליקציה שאפשר להריץ מכשיר Android או אמולטור.

AGP מכיל נקודות תוסף ליישומי פלאגין כדי לשלוט בקלט של build ולהרחיב אותו פונקציונליות שמבוססת על שלבים חדשים שאפשר לשלב עם גרסת build רגילה למשימות סיווג. בגרסאות הקודמות של 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 חדשים שמשמשים להתאמות אישיות נפוצות של גרסאות build, במתכונים לפלאגין של Android Gradle. הן מספקות דוגמאות להתאמות אישיות נפוצות של גרסאות build. אפשר גם למצוא עוד פרטים על ממשקי ה-API החדשים מאמרי עזרה

מידע בסיסי על גרסת Gradle

המדריך הזה לא כולל את כל מערכת ה-build של Gradle. עם זאת, היא כוללת קבוצת המושגים המינימלית שנדרשת כדי לעזור לכם לשלב את ממשקי ה-API שלנו, קישורים למסמכי התיעוד העיקריים של Gradle, להמשך קריאה.

אנחנו מניחים שיש לנו ידע בסיסי על האופן שבו Gradle פועלת, כולל אופן ההתקנה פרויקטים, לערוך קובצי build, להחיל יישומי פלאגין ולהריץ משימות. כדי ללמוד על העקרונות הבסיסיים של Gradle ביחס ל-AGP, מומלץ לעיין במאמר הגדרת build מידע נוסף על המסגרת הכללית להתאמה אישית יישומי פלאגין של Gradle, פיתוח יישומי פלאגין בהתאמה אישית ב-Gradle

מילון מונחים בנושא סוגי עצלנים

ב-Gradle יש כמה סוגים שמתנהגים באופן "מדורג", או עזרה בדחיית הצעת מחיר כבדה חישובים או Task של ה-build לשלבים מאוחרים יותר. הסוגים האלה נמצאים בראש סדר העדיפויות של ממשקי API של 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 מציע את וריאנט API והגישה לפלט של המשימות שלו, או ליצור ארטיפקטים, שתוכלו לקרוא ולבצע בהם טרנספורמציה. צפייה וריאנט API, Artifacts ו-Tasks לקבלת מידע נוסף.

שלבי פיתוח Gradle

מטבעם, בניית פרויקט היא תהליך מורכב ודורש משאבים, ויש תכונות שונות, כמו הימנעות מתצורה של משימות, את ההגדרות, ואת התכונה של שמירת הגדרות במטמון, שעוזרת למזער את זמן השימוש על חישובים מחדש או מיותרים.

כדי ליישם חלק מהאופטימיזציות האלה, הסקריפטים ויישומי הפלאגין של Gradle חייבים לציית לכללים מחמירים בכל אחד משלבי ה-build הייחודיים של Gradle: של אתחול, הגדרת תצורה וביצוע. במדריך הזה נתמקד את שלבי ההגדרה והביצוע. מידע נוסף על כל הסוגים השלבים המפורטים במדריך מחזור החיים של Gradle.

שלב ההגדרה

במהלך שלב ההגדרה, הסקריפטים של ה-build עבור כל הפרויקטים מתבצעת הערכה של ה-build, החלת יישומי הפלאגין ויחסי התלות של ה-build הבעיה נפתרה. צריך להשתמש בשלב הזה כדי להגדיר את ה-build באמצעות אובייקטים של DSL לרישום משימות ולקלט שלהן באופן מדורג.

כי שלב ההגדרה תמיד רץ, בלי קשר למשימה חשוב במיוחד לשמור על רשת צפופה ולהגביל או חישובים שמבוססים על מקורות קלט אחרים מלבד הסקריפטים של ה-build עצמם. כלומר, אסור להפעיל תוכנות חיצוניות או לקרוא מהרשת, או לבצע חישובים ארוכים שניתן לדחות לשלב הביצוע Task מופעים.

שלב הביצוע

בשלב הביצוע, המשימות המבוקשות והמשימות התלויות שלהן בוצעה. באופן ספציפי, Task שיטות הכיתות המסומנות ב-@TaskAction הן בוצעה. במהלך ביצוע המשימה, תהיה לכם הרשאה לקרוא ממקורות קלט (כמו קבצים) ולפתור ספקים עצלים באמצעות התקשרות אל Provider<T>.get(). פתרון של עצלנות ספקים בדרך הזו מתחילים רצף של שיחות map() או flatMap() לאחר מכן את פרטי התלות של המשימה שנכללים בספק. המשימות מופעלות כדי לממש את הערכים הנדרשים באופן מדורג.

וריאנט API, Artifacts ו-Tasks

וריאנט API הוא מנגנון תוספים בפלאגין Android Gradle שמאפשר לשנות את האפשרויות השונות, שבדרך כלל מוגדרות באמצעות ה-DSL ב-build קובצי תצורה שמשפיעים על גרסת ה-build של Android. גם וריאנט API מעניקה לכם גישה לתוצרי ביניים ולפריטי מידע סופיים שנוצרו על ידי build, כמו קובצי מחלקה, המניפסט הממוזג או קובצי APK/AAB.

תהליך ה-build של Android ונקודות התוסף

בזמן אינטראקציה עם AGP, צריך להשתמש במקום זאת בנקודות תוסף שנוצרו במיוחד רישום הקריאות החוזרות הטיפוסיות של מחזור החיים של Gradle (למשל, afterEvaluate()) או מגדירה יחסי תלות מפורשים של Task. משימות שנוצרו על ידי AGP נחשבות למשימות פרטי ההטמעה ולא נחשפים כ-API ציבורי. חובה להימנע מנסה לקבל מופעים של האובייקטים Task או לנחש את השמות של Task הוספת קריאות חוזרות (callbacks) או יחסי תלות אל האובייקטים של Task באופן ישיר.

AGP משלימה את השלבים הבאים כדי ליצור ולהפעיל את מכונות Task שלה, שבתורה יוצרת את הארטיפקטים של ה-build. השלבים העיקריים שנדרשים Variant ואחרי יצירת האובייקט מופיעות קריאות חוזרות שמאפשרות לבצע שינויים אובייקטים שנוצרו כחלק מ-build. חשוב לציין שכל קריאות חוזרות (callback) מתרחשות במהלך שלב ההגדרה (כפי שמתואר בדף הזה) והם חייבים לפעול במהירות, תוך דחיית כל עבודה מורכבת למופעי Task מתאימים במהלך שלב הביצוע במקום זאת.

  1. ניתוח DSL: מתבצעת הערכה של סקריפטים של build, נוצרים מאפיינים שונים של אובייקטי Android DSL מהבלוק android והגדירו. גם הקריאות החוזרות (callback) של וריאנט API שמתוארות בקטעים הבאים במהלך השלב הזה.
  2. finalizeDsl(): קריאה חוזרת (callback) שמאפשרת לשנות אובייקטי DSL לפני שהם נעול ליצירת רכיב (וריאנט). נוצרו VariantBuilder אובייקטים על סמך הנתונים באובייקטים של ה-DSL.

  3. נעילת DSL: DSL נעול ולא ניתן יותר לבצע שינויים.

  4. beforeVariants(): הקריאה החוזרת (callback) הזו יכולה להשפיע על הרכיבים שייווצרו, ועל חלק מהנכסים שלהם, דרך VariantBuilder. היא עדיין מאפשרת בתהליך ה-build ובארטיפקטים שנוצרים.

  5. יצירת וריאציות: רשימת הרכיבים ופריטי המידע שנוצרו בתהליך הפיתוח (Artifact) נוצרו סופיים ואי אפשר לשנות אותם.

  6. onVariants(): בקריאה החוזרת הזו תהיה לך גישה ל-Variant שנוצר ניתן להגדיר ערכים או ספקים לערכי Property שהם להכיל כדי לחשב אותו באופן מדורג.

  7. נעילת וריאנטים: האובייקטים של הווריאציות נעולים עכשיו והשינויים לא תקפים יותר ככל האפשר.

  8. משימות שנוצרו: Variant אובייקטים והערכים שלהם מסוג Property משמשים ל- יוצרים את המכונות Task שנחוצות לביצוע ה-build.

AGP משיקה AndroidComponentsExtension שמאפשר מתבצע רישום של קריאות חוזרות (callback) עבור finalizeDsl(), beforeVariants() ו-onVariants(). התוסף זמין בסקריפטים של build דרך הבלוק androidComponents:

// This is used only for configuring the Android build through DSL.
android { ... }

// The androidComponents block is separate from the DSL.
androidComponents {
   finalizeDsl { extension ->
      ...
   }
}

עם זאת, ההמלצה שלנו היא לשמור את סקריפטים של build רק עבור באמצעות ה-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)

בקריאה החוזרת הזו (callback) אפשר לגשת לאובייקטי ה-DSL שהיו שנוצר על ידי ניתוח המידע מהבלוק android בקובצי ה-build. אובייקטי ה-DSL האלה ישמשו לאתחול ולהגדרה של וריאנטים בעתיד בשלבי ה-build. לדוגמה, אפשר להשתמש באופן פרוגרמטי כדי ליצור או לשנות מאפיינים. עם זאת, חשוב לזכור שכל הערכים חייבים להיות נקבעות בזמן ההגדרה, כך שהן לא יכולות להסתמך על מקורות קלט חיצוניים. לאחר ביצוע הקריאה החוזרת (callback) הזה, האובייקטים של ה-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()

בשלב הזה של ה-build, יש גישה ל-VariantBuilder אובייקטים, הוא יקבע את הווריאנטים שייווצרו ואת המאפיינים שלהם. לדוגמה, אפשר להשבית באופן פרוגרמטי וריאנטים מסוימים, את הבדיקות שלהם או לשנות של הנכס (לדוגמה, minSdk) רק לווריאציה שנבחרה. דומה ל- finalizeDsl(), צריך לתקן את כל הערכים שסיפקת בשלב ההגדרה ולא תלויים בקלט חיצוני. אסור שהאובייקטים VariantBuilder יהיו ישתנה לאחר ביצוע הקריאה החוזרת של beforeVariants().

androidComponents {
    beforeVariants { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

הקריאה החוזרת (callback) של beforeVariants() לוקחת באופן אופציונלי VariantSelector, ואפשר להשיג באמצעות השיטה selector() ב-androidComponentsExtension. אפשר להשתמש בו כדי לסנן רכיבים שמשתתפים בהפעלות של קריאה חוזרת לפי את השם, את סוג הבנייה או את טעם המוצר.

androidComponents {
    beforeVariants(selector().withName("adfree")) { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

onVariants()

לפני הקריאה אל onVariants(), כל פריטי המידע שנוצרים בתהליך הפיתוח (Artifact) ייווצרו 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() })
}

הוספת מקורות שנוצרו ל-build

הפלאגין יכול לתרום מכמה סוגים של מקורות שנוצרו, כגון:

כדי לראות את הרשימה המלאה של המקורות שאפשר להוסיף, אפשר להיעזר ב 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. אפשר לקבל פרטים נוספים.

גישה לפריטי מידע שנוצרו בתהליך הפיתוח (Artifact) ושינוי שלהם

בנוסף לאפשרות לשנות מאפיינים פשוטים באובייקטים Variant, AGP מכיל גם מנגנון תוסף שמאפשר לקרוא או לבצע טרנספורמציה תוצרי הביניים והתוצרים הסופיים שנוצרו במהלך ה-build. לדוגמה, אפשר: קריאת התוכן הסופי של קובץ AndroidManifest.xml שמוזג בTask מותאם אישית, לנתח אותו, או להחליף את התוכן שלו לחלוטין בתוכן של קובץ מניפסט שנוצר על ידי Task בהתאמה אישית.

רשימה של פריטי מידע שנוצרו בתהליך הפיתוח (Artifact) שנתמכים כרגע זמינה בחומר העזר עבור הכיתה Artifact. לכל סוג של פריט מידע שנוצר בתהליך פיתוח (Artifact) יש תכונות שכדאי לדעת:

עוצמה (cardinality)

העוצמה של Artifact מייצגת את מספר הFileSystemLocation שלו או מספר הקבצים או הספריות מסוג פריט המידע שנוצר בתהליך הפיתוח (Artifact). אפשר קבלת מידע על העוצמה של פריט מידע שנוצר בתהליך פיתוח (Artifact) על ידי בדיקת ההורה שלו מחלקה: ארטיפקטים עם FileSystemLocation יחיד יהיו תת-מחלקה של Artifact.Single; פריטי מידע שנוצרו בתהליך פיתוח (Artifact) עם מספר מופעים של FileSystemLocation להיות תת-מחלקה של Artifact.Multiple.

סוג FileSystemLocation

כדי לבדוק אם Artifact מייצג קבצים או ספריות, אפשר לעיין סוג FileSystemLocation עם פרמטרים, שיכול להיות RegularFile או Directory.

פעולות נתמכות

כל מחלקה Artifact יכולה להטמיע כל אחד מהממשקים הבאים כדי לציין באילו פעולות הוא תומך:

  • Transformable: מאפשר להשתמש ב-Artifact כקלט ל-Task מבצע טרנספורמציות שרירותיות עליו ומפיק גרסה חדשה של Artifact
  • Appendable: חלה רק על פריטי מידע שנוצרו בתהליך הפיתוח (Artifact) שהם מחלקות משנה של Artifact.Multiple המשמעות היא שניתן להוסיף את Artifact אל, כלומר בהתאמה אישית עם המאפיין Task, ניתן ליצור מופעים חדשים מסוג Artifact הזה, שיתווספו לרשימה הקיימת.
  • Replaceable: חלה רק על פריטי מידע שנוצרו בתהליך הפיתוח (Artifact) שהם מחלקות משנה של Artifact.Single ניתן להחליף Artifact שניתן להחלפה במכשיר חדש לגמרי שמופק כפלט של Task.

בנוסף לשלוש הפעולות לשינוי ארטיפקטים, כל ארטיפקט תומך get() (או getAll()) פעולה שמחזירה Provider עם הגרסה הסופית של פריט המידע שנוצר בתהליך הפיתוח (Artifact) (לאחר שכל הפעולות עליו יסתיימו).

יישומי פלאגין מרובים יכולים להוסיף לצינור עיבוד הנתונים כל מספר של פעולות על ארטיפקטים מהקריאה החוזרת (callback) של onVariants(), ו-AGP יבטיחו שהן יהיו משורשרות כראוי שכל המשימות ירוצו בזמן הנכון וארטיפקטים מופקים בצורה נכונה עודכן. כלומר, כשפעולה משנה פלט כלשהו על ידי צירוף, החלפה או טרנספורמציה שלהן, הפעולה הבאה תראה את הגרסה המעודכנת של ארטיפקטים האלה כקלט, וכן הלאה.

נקודת הכניסה לפעולות הרישום היא המחלקה Artifacts. קטע הקוד הבא מראה איך אפשר לקבל גישה למופע של Artifacts מנכס באובייקט Variant ב-onVariants() קריאה חוזרת.

לאחר מכן אפשר להעביר את TaskProvider בהתאמה אישית כדי לקבל TaskBasedOperation אובייקט (1), ולהשתמש בו כדי לחבר את הקלט והפלט שלו באמצעות אחד wiredWith* methods (2).

השיטה המדויקת שצריך לבחור תלויה בעוצמה (cardinality) וב סוג 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 למערכת ה-build: