הפלאגין של Android Gradle (AGP) הוא מערכת ה-build הרשמית לאפליקציות Android. היא כוללת תמיכה בתכנות של סוגים רבים ושונים של מקורות, וקישור שלהם יחד לאפליקציה שאפשר להריץ במכשיר Android פיזי או במהדמ.
AGP מכיל נקודות הרחבה לתוספים, שמאפשרות לשלוט בנתוני הקלט של ה-build ולהרחיב את הפונקציונליות שלו באמצעות שלבים חדשים שאפשר לשלב עם משימות build רגילות. בגרסאות קודמות של AGP, ממשקי ה-API הרשמיים לא היו מופרדים בבירור מהטמעות פנימיות. החל מגרסה 7.0, ל-AGP יש קבוצה של ממשקי API רשמיים ויציבים שאפשר להסתמך עליהם.
מחזור החיים של API ב-AGP
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, מומלץ לעיין במאמר הגדרת ה-build. במאמר פיתוח יישומי פלאגין מותאמים אישית ל-Gradle מוסבר על המסגרת הכללית להתאמה אישית של יישומי פלאגין ל-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
.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 במסמך הזה.
שלבי ה-build ב-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. בנוסף, Variant API מאפשר לכם לגשת לפריטי מידע ביניים וסופיים שנוצרים על ידי ה-build, כמו קובצי כיתה, המניפסט הממוזג או קובצי APK/AAB.
תהליך ה-build של Android ונקודות ההרחבה
כשאתם משתמשים ב-AGP, השתמשו בנקודות הרחבה שנוצרו במיוחד במקום לרשום את פונקציות ה-callbacks הרגילות של מחזור החיים של 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 ובפריטי ה-artifact שנוצרים.יצירת וריאנטים: רשימת הרכיבים והפריטים שייווצרו תהיה סופית ולא ניתן יהיה לשנות אותה.
onVariants()
: בקריאה החוזרת הזו אתם מקבלים גישה לאובייקטים מסוג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 כדי ללמוד איך ליצור פלאגין בפרויקט. דוגמה לרישום של קריאות החזרה (callbacks) מקוד הפלאגין:
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
נבחן מקרוב את קריאות ה-back-call הזמינות ואת סוגי התרחישים לדוגמה שבהם הפלאגין יכול לתמוך בכל אחת מהן:
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
אחרי סיום הביצוע של פונקציית ה-callback של beforeVariants()
.
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
אפשר להעביר ל-callback של beforeVariants()
את הפרמטר VariantSelector
, שאפשר לקבל באמצעות השיטה 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() })
}
הוספת מקורות שנוצרו לגרסה היציבה
הפלאגין יכול לתרום כמה סוגים של מקורות שנוצרו, כמו:
- קוד האפליקציה בספרייה
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
. לכל סוג של ארטיפקט יש מאפיינים מסוימים שחשוב לדעת:
עוצמה (cardinality)
העוצמה (cardinality) של 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
עם הגרסה הסופית של הארטיפקט (אחרי שכל הפעולות עליו מסתיימות).
אפשר להשתמש בכמה יישומי פלאגין כדי להוסיף לצינור עיבוד הנתונים מספר בלתי מוגבל של פעולות על ארטיפקטים מהקריאה החוזרת (callback) של 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
- הגדרה עצלה
- הימנעות מהגדרת משימות