הפלאגין של 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
המדריך הזה לא כולל את כל מערכת ה-build של Gradle. עם זאת, הוא מכיל את קבוצת המושגים המינימלית הנדרשת כדי לעזור לכם לשלב את ממשקי ה-API שלנו, ויש בו קישורים למסמכים הראשיים של Gradle לקריאה נוספת.
כנראה יש לנו ידע בסיסי על האופן שבו Gradle פועלת, כולל איך להגדיר פרויקטים, לערוך קובצי build, להחיל יישומי פלאגין ולהריץ משימות. מידע בסיסי על Gradle על AGP זמין במאמר Configure yourbuild. למידע על המסגרת הכללית להתאמה אישית של יישומי פלאגין של 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
. הפונקציה הלוגרית לא מבוצעת באופן מיידי, אלא ההפעלה שלה מושהית עד לרגע שבו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
.
הספקים והשיטות שלהם לטרנספורמציה חיוניים להגדרת קלט ופלט של משימות בצורה מדורגת, כלומר ללא צורך ליצור את כל המשימות מראש ולפתור את הערכים.
הספקים כוללים גם מידע על תלות בין משימות. כשיוצרים 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 ואת הגישה לפלט של המשימות שלו, או פריטי build, שאפשר לקרוא ולבצע בהם טרנספורמציה. מידע נוסף זמין בקטע Variant API, Artifacts and Tasks במסמך הזה.
שלבי פיתוח Gradle
פיתוח פרויקט הוא תהליך מורכב שדורש משאבים רבים, ויש תכונות שונות כמו הימנעות מהגדרת משימות, בדיקות עדכניות ותכונה של שמירת הגדרות במטמון, שעוזרות לצמצם את הזמן שדרוש לחישובים שניתן לשחזור או לחישובים מיותרים.
כדי ליישם חלק מהאופטימיזציות האלה, הסקריפטים ויישומי הפלאגין של Gradle צריכים לציית לכללים מחמירים בכל אחד משלבי ה-build הייחודיים של Gradle: אתחול, הגדרה וביצוע. במדריך הזה נתמקד בשלבי ההגדרה והביצוע. מידע נוסף על כל השלבים זמין במדריך למחזור החיים של ה-build ב-Gradle.
שלב ההגדרה
במהלך שלב ההגדרה, מתבצעת הערכה של הסקריפטים של ה-build עבור כל הפרויקטים שהם חלק מה-build, החלת יישומי הפלאגין ופתרון יחסי התלות של ה-build. צריך להשתמש בשלב הזה כדי להגדיר את ה-build באמצעות אובייקטי DSL ולרישום משימות ואת הקלט שלהן באופן מדורג.
מכיוון ששלב ההגדרה תמיד רץ, בלי קשר למשימה שמבקשים להפעיל, חשוב במיוחד לשמור על תנועה מצומצמת ולהגביל את החישובים בהתאם לקלט מלבד הסקריפטים של ה-build עצמם.
כלומר אסור להפעיל תוכנות חיצוניות או לקרוא אותן מהרשת, ואסור לבצע חישובים ארוכים שאפשר לדחות לשלב הביצוע כמכונות Task
מתאימות.
שלב הביצוע
בשלב הביצוע מתבצעות המשימות המבוקשות והמשימות התלויות בהן. באופן ספציפי, מתבצעת ההפעלה של שיטות הכיתה Task
שמסומנות ב-@TaskAction
. במהלך ביצוע המשימה, מותר לקרוא מהקלטים (כמו קבצים) ולפתור ספקים עצלים באמצעות קריאה ל-Provider<T>.get()
. פתרון ספקים עצלים בדרך הזו מפעיל רצף של קריאות ל-map()
או ל-flatMap()
, בהתאם למידע על יחסי התלות בין המשימות שמכיל הספק. המשימות מופעלות באופן עצל כדי להפוך את הערכים הנדרשים לממשיים.
Variant API, Artifacts ו-Tasks
Variant API הוא מנגנון הרחבה בפלאגין Android Gradle שמאפשר לבצע פעולות שונות על האפשרויות השונות, שבדרך כלל מוגדרות באמצעות DSL בקובצי התצורה של ה-build, שמשפיעות על ה-build של Android. וריאנט API מאפשר גם גישה לתוצרי ביניים ופריטי מידע סופיים שנוצרים על ידי ה-build, כמו קובצי מחלקה, המניפסט הממוזג או קובצי APK/AAB.
תהליך ה-build ונקודות התוסף ב-Android
באינטראקציה עם AGP, צריך להשתמש בנקודות תוסף שנוצרו במיוחד במקום לרשום את הקריאות החוזרות הטיפוסיות של מחזור החיים של Gradle (למשל afterEvaluate()
) או להגדיר יחסי תלות מפורשים ב-Task
. משימות שנוצרות על ידי AGP נחשבות כפרטים להטמעה ולא נחשפות כ-API ציבורי. אסור לנסות לקבל מופעים של אובייקטי Task
או לנחש את השמות של Task
ולהוסיף קריאות חזרה או יחסי תלות לאובייקטים האלה של Task
באופן ישיר.
AGP משלימה את השלבים הבאים כדי ליצור ולהפעיל את מכונות Task
, שבתוכה יוצרים את הארטיפקטים של ה-build. אחרי השלבים העיקריים של יצירת האובייקט Variant
, מגיעים קריאות חזרה (callbacks) שמאפשרות לבצע שינויים באובייקטים מסוימים שנוצרו כחלק מ-build. חשוב לציין שכל הקריאות החוזרות מתרחשות במהלך שלב ההגדרה (כפי שמתואר בדף הזה) וצריכות לפעול במהירות, וכך לדחות את כל הקריאות המורכבות לפעולה למכונות Task
מתאימות במהלך שלב הביצוע.
- ניתוח של שפת DSL: בשלב הזה מתבצעת הערכה של סקריפטים ל-build, וכן היצירה וההגדרה של המאפיינים השונים של אובייקטי ה-DSL של Android מהבלוק
android
. גם קריאות החזרה (callbacks) של Variant API שמתוארות בקטעים הבאים נרשמות בשלב הזה. finalizeDsl()
: קריאה חוזרת (callback) שמאפשרת לשנות אובייקטים של DSL לפני שהם ננעלים ליצירת רכיב (וריאנט). אובייקטים מסוגVariantBuilder
נוצרים על סמך הנתונים הכלולים באובייקטי ה-DSL.נעילת DSL: ה-DSL נעול עכשיו ואי אפשר לבצע בו שינויים.
beforeVariants()
: קריאה חוזרת (callback) זו יכולה להשפיע על הרכיבים שייווצרו ועל חלק מהמאפיינים שלהם באמצעותVariantBuilder
. היא עדיין מאפשרת לבצע שינויים בתהליך ה-build ובארטיפקטים שנוצרים.יצירת וריאציות: רשימת הרכיבים וארטיפקטים שייווצרו היא סופית ואי אפשר לשנות אותה.
onVariants()
: בקריאה החוזרת (callback) הזה מקבלים גישה לאובייקטיםVariant
שנוצרו, ואפשר להגדיר ערכים או ספקים לערכיProperty
שהם מכילים, כך שהם יחושבו באופן מדורג.נעילת וריאנטים: האובייקטים של הווריאציות נעולים עכשיו ואי אפשר לבצע שינויים יותר.
משימות שנוצרו: אובייקטים מסוג
Variant
והערכים שלהם ב-Property
משמשים ליצירת המופעים שלTask
שנדרשים לביצוע ה-build.
ב-AGP יש פונקציה חדשה בשם AndroidComponentsExtension
שמאפשרת לרשום פונקציות חזרה (callbacks) ל-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
}
}
לחלופין, הקריאה החוזרת של beforeVariants()
היא באמצעות VariantSelector
, ואפשר לקבל אותה באמצעות ה-method selector()
ב-androidComponentsExtension
. אפשר להשתמש בו כדי לסנן רכיבים שמשתתפים בהפעלת הקריאה החוזרת (callback) על סמך השם, סוג ה-build או גרסת המוצר שלהם.
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() })
}
הוספת מקורות שנוצרו ל-build
הפלאגין יכול לתרום מכמה סוגים של מקורות שנוצרו, כגון:
- קוד האפליקציה בספרייה
java
- משאבי Android בספרייה
res
- משאבי Java
בספרייה
resources
- נכסי Android בספרייה
assets
בממשק 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) יש תכונות שכדאי לדעת:
עוצמה (cardinality)
העוצמה (cardinality) של Artifact
מייצגת את מספר המופעים של FileSystemLocation
, או את מספר הקבצים או הספריות מסוג הארטיפקט. אפשר לקבל מידע על העוצמה של פריט מידע שנוצר בתהליך הפיתוח (cardinality) באמצעות בדיקת המחלקה הראשית שלו: פריטי מידע שנוצרו בתהליך פיתוח (Artifact) עם 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
ל-method שמייצג את הפעולה שנבחרה באובייקט *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
שיתאימו לשילובים אחרים של עוצמת הקבוצה (cardinality) של Artifact
וסוגים של FileSystemLocation
.
למידע נוסף על הרחבת AGP, מומלץ לקרוא את הקטעים הבאים במדריך למערכת ה-build של Gradle:
- פיתוח פלאגינים מותאמים אישית של Gradle
- הטמעה של יישומי פלאגין של Gradle
- פיתוח סוגים של משימות מסוג Gradle בהתאמה אישית
- הגדרה עצלה
- הימנעות מהגדרת משימות