פלאגין Android ל-Gradle (AGP) הוא מערכת הבנייה הרשמית לאפליקציות Android. הוא כולל תמיכה בהידור של סוגים רבים ושונים של מקורות וקישור שלהם לאפליקציה שאפשר להפעיל במכשיר Android פיזי או באמולטור.
AGP מכיל נקודות הרחבה לתוספים שמאפשרות לשלוט בקלט של הבנייה ולהרחיב את הפונקציונליות שלו באמצעות שלבים חדשים שאפשר לשלב עם משימות בנייה רגילות. בגרסאות קודמות של AGP לא היו ממשקי API רשמיים שהופרדו בצורה ברורה מהטמעות פנימיות. החל מגרסה 7.0, ל-AGP יש קבוצה של ממשקי API רשמיים ויציבים שאפשר להסתמך עליהם.
מחזור החיים של AGP API
AGP פועל לפי מחזור החיים של התכונות ב-Gradle כדי לציין את מצב ממשקי ה-API שלו:
- פנימי: לא מיועד לשימוש ציבורי
- בשלבי פיתוח: זמינות לשימוש ציבורי אבל לא סופיות, כלומר יכול להיות שהן לא יהיו תואמות לאחור בגרסה הסופית
- ציבורית: זמינה לשימוש ציבורי ויציבה
- הוצא משימוש: לא נתמך יותר, והוחלף בממשקי API חדשים
מדיניות הוצאה משימוש
ה-AGP מתפתח עם הוצאה משימוש של ממשקי API ישנים והחלפה שלהם בממשקי API חדשים ויציבים ובשפה חדשה ספציפית לדומיין (DSL). השינוי הזה יתבצע בכמה גרסאות של AGP, ומידע נוסף זמין בציר הזמן של המעבר ל-AGP API/DSL.
כשמוציאים משימוש ממשקי AGP API, לצורך ההעברה הזו או מסיבות אחרות, הם ממשיכים להיות זמינים בגרסה הראשית הנוכחית, אבל מוצגות אזהרות. ממשקי API שהוצאו משימוש יוסרו לחלוטין מ-AGP בגרסה הראשית הבאה. לדוגמה, אם הוצא משימוש ממשק API ב-AGP 7.0, הוא יהיה זמין בגרסה הזו ויציג אזהרות. ממשק ה-API הזה לא יהיה זמין יותר ב-AGP 8.0.
כדי לראות דוגמאות לשימוש בממשקי API חדשים בהתאמות אישיות נפוצות של גרסאות build, אפשר לעיין במתכונים ל-Android Gradle plugin. הם כוללים דוגמאות להתאמות אישיות נפוצות של בנייה. בנוסף, במאמרי העזרה מפורטים פרטים נוספים על ממשקי ה-API החדשים.
הבסיס של בניית Gradle
המדריך הזה לא מכסה את כל מערכת ה-build של Gradle. עם זאת, הוא כולל את קבוצת המושגים המינימלית שצריך להכיר כדי לשלב את ה-API שלנו, וכולל קישורים למסמכי Gradle הראשיים לקריאה נוספת.
אנחנו מניחים שיש לכם ידע בסיסי לגבי אופן הפעולה של Gradle, כולל איך להגדיר פרויקטים, לערוך קובצי build, להחיל פלאגינים ולהריץ משימות. כדי להבין את היסודות של Gradle בהקשר של AGP, מומלץ לעיין במאמר בנושא הגדרת תהליך הבנייה. כדי לקבל מידע על המסגרת הכללית להתאמה אישית של תוספים ל-Gradle, אפשר לעיין במאמר פיתוח תוספים מותאמים אישית ל-Gradle.
מילון מונחים של סוגים עצלים ב-Gradle
ל-Gradle יש כמה סוגים שמתנהגים בצורה 'עצלנית', או עוזרים לדחות חישובים כבדים או יצירה לשלבים מאוחרים יותר של הבנייה.Task
הסוגים האלה הם הבסיס להרבה ממשקי 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>
- Implements
Provider<T>
, so it also provides a value of typeT
. בניגוד ל-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
, הפלט הזה הופך לתלות מרומזת של Provider
, והוא ייווצר ויפעל בכל פעם שהערך של Provider
נפתר, למשל כשנדרש על ידי Task
אחר.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 artifacts, שאפשר לקרוא ולשנות. מידע נוסף זמין במאמר Variant API, Artifacts, and Tasks.
שלבי בנייה ב-Gradle
בניית פרויקט היא תהליך מורכב שדורש משאבים רבים. יש תכונות שנועדו לצמצם את הזמן שמושקע בחישובים שניתן לשחזר או בחישובים מיותרים, כמו הימנעות מהגדרת משימות, בדיקות עדכניות ותכונת שמירת ההגדרות במטמון.
כדי להחיל חלק מהאופטימיזציות האלה, סקריפטים ותוספים של Gradle צריכים לפעול לפי כללים מחמירים במהלך כל אחד משלבי ה-build הנפרדים של Gradle: אתחול, הגדרה וביצוע. במדריך הזה נתמקד בשלבי ההגדרה והביצוע. מידע נוסף על כל השלבים זמין במדריך למחזור החיים של Gradle build.
שלב ההגדרה
במהלך שלב ההגדרה, המערכת מעריכה את סקריפטים הבנייה של כל הפרויקטים שמשתתפים בבנייה, מחילה את התוספים ומזהה את התלות בבנייה. בשלב הזה צריך להגדיר את ה-build באמצעות אובייקטים של DSL, ולרשום משימות ואת הקלט שלהן באופן עצלני.
מכיוון ששלב ההגדרה תמיד מופעל, לא משנה איזו משימה מופעלת, חשוב במיוחד לשמור על יעילות ולמנוע ממחשובים להסתמך על קלטים אחרים מלבד סקריפטים של בנייה.
כלומר, לא מריצים תוכניות חיצוניות, לא קוראים מהרשת ולא מבצעים חישובים ארוכים שאפשר לדחות לשלב הביצוע בתור מופעים מתאימים של Task
.
שלב הביצוע
בשלב הביצוע, המשימות המבוקשות והמשימות שתלויות בהן מבוצעות. באופן ספציפי, מופעלות שיטות המחלקה Task
שמסומנות ב-@TaskAction
. במהלך ביצוע המשימה, מותר לקרוא מקבצי קלט (כמו קבצים) ולפתור ספקי lazy על ידי קריאה ל-Provider<T>.get()
. פתרון של lazy
providers בדרך הזו מפעיל רצף של קריאות map()
או flatMap()
שפועלות לפי
מידע על תלות במשימות שכלול ב-provider. המשימות מופעלות באופן עצלני כדי להפיק את הערכים הנדרשים.
Variant API, Artifacts ו-Tasks
Variant API הוא מנגנון הרחבה בפלאגין Android Gradle שמאפשר לכם לשנות את האפשרויות השונות, שבדרך כלל מוגדרות באמצעות DSL בקובצי הגדרות של build, שמשפיעות על ה-build של Android. ה-Variant API גם מאפשר לכם לגשת לארטיפקטים ביניים וסופיים שנוצרים על ידי ה-build, כמו קובצי class, המניפסט הממוזג או קובצי APK/AAB.
תהליך ה-build של Android ונקודות ההרחבה
כשמבצעים אינטראקציה עם AGP, צריך להשתמש בנקודות הרחבה שנוצרו במיוחד במקום לרשום את קריאות החזרה הרגילות של מחזור החיים של Gradle (כמו afterEvaluate()
) או להגדיר תלות מפורשת ב-Task
. משימות שנוצרו על ידי AGP נחשבות לפרטי הטמעה ולא מוצגות כ-API ציבורי. אסור לנסות לקבל מופעים של אובייקטים מסוג Task
או לנחש את השמות של Task
ולהוסיף קריאות חוזרות או תלות לאובייקטים האלה ישירות.Task
תהליך העבודה של AGP כולל את השלבים הבאים ליצירה ולהפעלה של מופעי Task
,
שבתורם יוצרים את תוצרי הבנייה. אחרי השלבים העיקריים שקשורים ליצירת אובייקט Variant
, מתבצעות קריאות חוזרות שמאפשרות לכם לבצע שינויים באובייקטים מסוימים שנוצרו כחלק מהבנייה. חשוב לציין שכל הקריאות החוזרות מתרחשות במהלך שלב ההגדרה (שמתואר בדף הזה) והן חייבות לפעול במהירות. במקום זאת, כל פעולה מורכבת צריכה להתבצע במהלך שלב ההרצה על ידי מופעים מתאימים של Task
.
- ניתוח של DSL: בשלב הזה מתבצעת הערכה של סקריפטים לבנייה, ונוצרים ומוגדרים המאפיינים השונים של אובייקטים של Android DSL מהבלוק
android
. במהלך השלב הזה נרשמים גם הקבלאקים של Variant API שמתוארים בקטעים הבאים.
finalizeDsl()
: קריאה חוזרת שמאפשרת לשנות אובייקטים של DSL לפני שהם ננעלים ליצירת רכיב (וריאנט). אובייקטים מסוגVariantBuilder
נוצרים על סמך נתונים שכלולים באובייקטים של DSL.נעילת DSL: ה-DSL נעול עכשיו ואי אפשר לבצע יותר שינויים.
beforeVariants()
: הקריאה החוזרת הזו יכולה להשפיע על הרכיבים שנוצרים ועל חלק מהמאפיינים שלהם, באמצעותVariantBuilder
. אפשר עדיין לבצע שינויים בתהליך הבנייה ובארטיפקטים שנוצרים.יצירת וריאנטים: רשימת הרכיבים והארטיפקטים שייווצרו סופית ואי אפשר לשנות אותה.
onVariants()
: בפונקציית הקריאה החוזרת הזו, מקבלים גישה לאובייקטים שנוצרוVariant
ואפשר להגדיר ערכים או ספקים לערכיםProperty
שהם מכילים, כדי שהחישוב יתבצע רק כשצריך.נעילת וריאציות: אובייקטים של וריאציות נעולים עכשיו ואי אפשר לבצע בהם שינויים.
משימות שנוצרו: נעשה שימוש באובייקטים של
Variant
ובערכים שלProperty
כדי ליצור את המופעים שלTask
שנדרשים לביצוע הבנייה.
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. דוגמה לרישום של פונקציות ה-callback מקוד הפלאגין:
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
בטבלה הבאה מפורטים סוגי ה-callback שזמינים וסוגי תרחישי השימוש שהתוסף יכול לתמוך בהם בכל אחד מהם:
finalizeDsl(callback: (DslExtensionT) -> Unit)
בפונקציית הקריאה החוזרת הזו, אפשר לגשת לאובייקטים של DSL שנוצרו על ידי ניתוח המידע מהבלוק android
בקובצי ה-build ולשנות אותם.
אובייקטי ה-DSL האלה ישמשו לאתחול ולהגדרה של וריאציות בשלבים מאוחרים יותר של ה-build. לדוגמה, אתם יכולים ליצור באופן פרוגרמטי הגדרות חדשות או לשנות מאפיינים, אבל חשוב לזכור שכל הערכים צריכים להיות מוגדרים בזמן ההגדרה, ולכן הם לא יכולים להסתמך על קלט חיצוני.
אחרי שהפונקציה הזו מסיימת את ההרצה שלה, אובייקטי ה-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
. אפשר להשתמש בו כדי לסנן רכיבים שמשתתפים בהפעלת הקריאה החוזרת על סמך השם, סוג ה-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() })
}
הוספת מקורות שנוצרו על ידי AI לבנייה
תוסף יכול לתרום כמה סוגים של מקורות שנוצרו, כמו:
- קוד האפליקציה בספרייה
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
source set. התהליך דומה לסוגים אחרים של מקורות.
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 recipe.
גישה לפריטי מידע שנוצרים בתהליך פיתוח (Artifact) ושינוי שלהם
בנוסף לאפשרות לשנות מאפיינים פשוטים באובייקטים Variant
, AGP כולל גם מנגנון הרחבה שמאפשר לקרוא או לשנות ארטיפקטים ביניים וארטיפקטים סופיים שנוצרו במהלך הבנייה. לדוגמה, אתם יכולים לקרוא את התוכן של קובץ 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
עם הגרסה הסופית של הארטיפקט (אחרי שכל הפעולות בו הסתיימו).
תוספים מרובים יכולים להוסיף כל מספר של פעולות על ארטיפקטים לצינור הנתונים מתוך הקריאה החוזרת onVariants()
, ו-AGP ידאג שהם ישורשרו בצורה נכונה כדי שכל המשימות יפעלו בזמן הנכון והארטיפקטים ייווצרו ויתעדכנו בצורה נכונה. המשמעות היא שכאשר פעולה משנה פלטים על ידי הוספה, החלפה או שינוי שלהם, הפעולה הבאה תראה את הגרסה המעודכנת של הארטיפקטים האלה כפלטים, וכן הלאה.
נקודת הכניסה לרישום פעולות היא המחלקה Artifacts
.
בקטע הקוד הבא אפשר לראות איך מקבלים גישה למופע של Artifacts
ממאפיין באובייקט Variant
בקריאה החוזרת onVariants()
.
לאחר מכן תוכלו להעביר את TaskProvider
המותאם אישית כדי לקבל אובייקט TaskBasedOperation
(1), ולהשתמש בו כדי לחבר את הקלט והפלט שלו באמצעות אחת מהשיטות של wiredWith*
(2).
השיטה המדויקת שצריך לבחור תלויה בקרדינליות ובFileSystemLocation
סוג שהוטמעו על ידי Artifact
שרוצים לשנות.
לבסוף, מעבירים את ה-type 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 build:
- פיתוח פלאגינים מותאמים אישית של Gradle
- הטמעה של פלאגינים של Gradle
- פיתוח סוגים מותאמים אישית של משימות Gradle
- הגדרה של Lazy Configuration
- Task Configuration Avoidance