במדריך לתחילת העבודה מוסבר איך ליצור WorkRequest ולהוסיף אותו לתור.
במדריך הזה תלמדו איך להגדיר ולהתאים אישית אובייקטים של WorkRequest כדי לטפל בתרחישי שימוש נפוצים, כמו:
- תזמון עבודה חד-פעמית ועבודה חוזרת
- הגדרת מגבלות עבודה, כמו דרישה לחיבור Wi-Fi או לטעינה
- הבטחת עיכוב מינימלי בביצוע העבודה
- הגדרת אסטרטגיות של ניסיון חוזר והשהיה
- העברת נתוני קלט לעבודה
- קיבוץ של עבודה שקשורה אחת לשנייה באמצעות תגים
סקירה כללית
עבודה מוגדרת ב-WorkManager באמצעות WorkRequest. כדי לתזמן עבודה באמצעות WorkManager, קודם צריך ליצור אובייקט WorkRequest ואז להוסיף אותו לתור.
Kotlin
val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)
Java
WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);
אובייקט WorkRequest מכיל את כל המידע שנדרש ל-WorkManager כדי לתזמן ולהריץ את העבודה. הוא כולל אילוצים שצריכים להתקיים כדי שהעבודה תפעל, מידע על תזמון כמו עיכובים או מרווחי זמן חוזרים, הגדרות ניסיון חוזר, ויכול לכלול נתוני קלט אם העבודה מסתמכת עליהם.
WorkRequest עצמו הוא מחלקה בסיסית מופשטת. יש שתי הטמעות נגזרות של המחלקה הזו שבהן אפשר להשתמש כדי ליצור את הבקשה, OneTimeWorkRequest ו-PeriodicWorkRequest.
כפי שאפשר להבין מהשמות שלהם, OneTimeWorkRequest שימושי לתזמון של עבודה שלא חוזרת על עצמה, ואילו PeriodicWorkRequest מתאים יותר לתזמון של עבודה שחוזרת על עצמה במרווחי זמן מסוימים.
תזמון עבודה חד-פעמית
לשימוש בסיסי שלא דורש הגדרות נוספות, משתמשים בשיטה הסטטית from:
Kotlin
val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
Java
WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);
למשימות מורכבות יותר, אפשר להשתמש בכלי לבניית תרשימים:
Kotlin
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
// Additional configuration
.build()
Java
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
// Additional configuration
.build();
תזמון של משימות דחופות
ב-WorkManager 2.7.0 הוצג המושג 'משימה דחופה'. כך WorkManager יכול לבצע עבודה חשובה, ומערכת ההפעלה יכולה לשלוט טוב יותר בגישה למשאבים.
מאפיינים של משימות בעדיפות גבוהה:
- חשוב לדעת: האצת העבודה מתאימה למשימות שחשובות למשתמש או למשימות שהמשתמש יזם.
- מהירות: עבודה מהירה מתאימה למשימות קצרות שמתחילות מיד ומסתיימות תוך כמה דקות.
- מכסות: מכסה ברמת המערכת שמגבילה את זמן ההפעלה בחזית קובעת אם אפשר להתחיל משימה בעדיפות גבוהה.
- ניהול צריכת החשמל: הגבלות על ניהול צריכת החשמל, כמו חיסכון בסוללה ומצב שינה, לא צפויות להשפיע על משימה דחופה.
- זמן אחזור (Latency): המערכת מבצעת באופן מיידי עבודה שדורשת טיפול מהיר, בתנאי שעומס העבודה הנוכחי של המערכת מאפשר זאת. כלומר, העבודה הזו רגישה לזמן אחזור ולא ניתן לתזמן אותה לביצוע מאוחר יותר.
תרחיש שימוש פוטנציאלי לעבודה מזורזת יכול להיות באפליקציית צ'אט, כשהמשתמש רוצה לשלוח הודעה או תמונה מצורפת. באופן דומה, אפליקציה שמטפלת בתהליך תשלום או בתהליך הרשמה למינוי יכולה גם להשתמש בעבודה מזורזת. הסיבה לכך היא שהמשימות האלה חשובות למשתמש, הן מתבצעות במהירות ברקע, הן צריכות להתחיל מיד והן צריכות להמשיך להתבצע גם אם המשתמש סוגר את האפליקציה.
מכסות
כדי שמשימה תפעל, המערכת צריכה להקצות לה זמן ביצוע. זמן הביצוע לא בלתי מוגבל. במקום זאת, כל אפליקציה מקבלת מכסת זמן ריצה. אם האפליקציה משתמשת בזמן הביצוע שלה ומגיעה למכסה שהוקצתה לה, אי אפשר יותר לבצע עבודה מהירה עד שהמכסה מתעדכנת. כך מערכת Android יכולה לאזן בצורה יעילה יותר את המשאבים בין האפליקציות.
כמות זמן הביצוע שזמינה לאפליקציה מבוססת על הקטגוריה של מצב המתנה ועל חשיבות התהליך.
אתם יכולים לקבוע מה יקרה אם המכסה של ההרצה לא תאפשר להריץ משימה בעדיפות גבוהה באופן מיידי. פרטים נוספים מופיעים בקטעי הקוד הבאים.
ביצוע משימה דחופה
החל מ-WorkManager 2.7, האפליקציה יכולה להתקשר אל setExpedited() כדי להצהיר ש-WorkRequest צריך לפעול כמה שיותר מהר באמצעות עבודה מזורזת. בקטע הקוד הבא יש דוגמה לאופן השימוש ב-setExpedited():
Kotlin
val request = OneTimeWorkRequestBuilder<SyncWorker>()
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build()
WorkManager.getInstance(context)
.enqueue(request)
Java
OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
.setInputData(inputData)
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build();
בדוגמה הזו, אנחנו מאתחלים מופע של OneTimeWorkRequest וקוראים ל-setExpedited(). הבקשה הזו הופכת למשימה דחופה. אם המכסה מאפשרת זאת, התהליך יתחיל לפעול ברקע באופן מיידי. אם נעשה שימוש במכסה, הפרמטר OutOfQuotaPolicy מציין שהבקשה צריכה לפעול כרגיל, ללא עדיפות.
תאימות לאחור ושירותים שפועלים בחזית
כדי לשמור על תאימות לאחור למשימות מואצות, יכול להיות ש-WorkManager יפעיל שירות חזיתי בגרסאות פלטפורמה ישנות יותר מ-Android 12. שירותים שפועלים בחזית יכולים להציג התראה למשתמש.
השיטות getForegroundInfoAsync() ו-getForegroundInfo() ב-Worker מאפשרות ל-WorkManager להציג התראה כשקוראים ל-setExpedited() בגרסאות של Android לפני גרסה 12.
כל ListenableWorker חייב להטמיע את השיטה getForegroundInfo אם רוצים לבקש שהמשימה תפעל כמשימה בעדיפות גבוהה.
כשמטרגטים ל-Android 12 ואילך, השירותים שפועלים בחזית נשארים זמינים באמצעות השיטה המתאימה setForeground.
קובץ שירות
העובדים לא יודעים אם העבודה שהם מבצעים היא דחופה או לא. אבל
בגרסאות מסוימות של Android, העובדים יכולים להציג התראה כשמזרזים WorkRequest.
כדי להפעיל את זה, WorkManager מספק את getForegroundInfoAsync() method,
שצריך להטמיע כדי ש-WorkManager יוכל להציג התראה כדי להתחיל ForegroundService בשבילכם כשצריך.
CoroutineWorker
אם אתם משתמשים ב-CoroutineWorker, אתם צריכים להטמיע getForegroundInfo(). אחר כך מעבירים אותו אל setForeground() בתוך doWork(). הפעולה הזו תיצור את ההתראה בגרסאות של Android שקודמות לגרסה 12.
דוגמה:
class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):
CoroutineWorker(appContext, workerParams) {
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
NOTIFICATION_ID, createNotification()
)
}
override suspend fun doWork(): Result {
TODO()
}
private fun createNotification() : Notification {
TODO()
}
}
כללי מדיניות בנושא מכסות
אתם יכולים לקבוע מה יקרה לעבודה שמוגדרת כדחופה כשהאפליקציה תגיע למכסת הביצוע שלה. כדי להמשיך, אפשר להעביר את setExpedited():
-
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, מה שגורם להרצת המשימה כבקשת עבודה רגילה. אפשר לראות את זה בקטע הקוד הקודם. -
OutOfQuotaPolicy.DROP_WORK_REQUEST, מה שגורם לביטול הבקשה אם אין מספיק מכסה.
משימה דחופה שנדחתה
המערכת מנסה להפעיל משימה בעדיפות גבוהה בהקדם האפשרי אחרי שהמשימה מופעלת. עם זאת, כמו במקרים אחרים של עבודות, המערכת עשויה לדחות את ההתחלה של עבודות חדשות שדורשות טיפול מהיר, למשל במקרים הבאים:
- עומס: עומס המערכת גבוה מדי. זה יכול לקרות אם כבר פועלות יותר מדי משימות, או אם אין למערכת מספיק זיכרון.
- מכסה: חרגתם ממגבלת המכסה של משימות בעדיפות גבוהה. העבודה המואצת מתבססת על מערכת מכסות שמתבססת על דליים של אפליקציות במצב המתנה, ומגבילה את זמן הביצוע המקסימלי בתוך חלון זמן מתגלגל. המיכסות שמשמשות לעבודה מזורזת הן מגבילות יותר מאלה שמשמשות לסוגים אחרים של משימות ברקע.
תזמון עבודה תקופתית
יכול להיות שהאפליקציה שלכם תדרוש מדי פעם הפעלה של עבודות מסוימות. לדוגמה, יכול להיות שתרצו לגבות את הנתונים שלכם מדי פעם, להוריד תוכן חדש באפליקציה או להעלות יומנים לשרת.
כך משתמשים ב-PeriodicWorkRequest כדי ליצור אובייקט WorkRequest שמופעל באופן מחזורי:
Kotlin
val saveRequest =
PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
// Additional configuration
.build()
Java
PeriodicWorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
// Constraints
.build();
בדוגמה הזו, העבודה מתוזמנת במרווח של שעה.
תקופת המרווח מוגדרת כזמן המינימלי בין חזרות. הזמן המדויק שבו יופעל ה-Worker תלוי באילוצים שבהם אתם משתמשים באובייקט WorkRequest ובאופטימיזציות שהמערכת מבצעת.
מרווחי זמן גמישים להרצה
אם אופי העבודה שלכם רגיש לתזמון ההרצה, אתם יכולים להגדיר את PeriodicWorkRequest להרצה בתקופת גמישות בתוך כל תקופת מרווח, כמו שמוצג באיור 1.

איור 1. דיאגרמה מציגה מרווחי זמן חוזרים עם התקופה הגמישה שבה העבודה יכולה לפעול.
כדי להגדיר עבודה תקופתית עם תקופת גמישות, מעבירים את flexInterval יחד עם repeatInterval כשיוצרים את PeriodicWorkRequest. תקופת הגמישות מתחילה ב-repeatInterval - flexInterval ומסתיימת בסוף פרק הזמן.
הדוגמה הבאה היא של עבודה תקופתית שיכולה להתבצע במהלך 15 הדקות האחרונות של כל תקופה של שעה.
Kotlin
val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
1, TimeUnit.HOURS, // repeatInterval (the period cycle)
15, TimeUnit.MINUTES) // flexInterval
.build()
Java
WorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
1, TimeUnit.HOURS,
15, TimeUnit.MINUTES)
.build();
מרווח החזרה חייב להיות גדול מ-PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS או שווה לו, ומרווח הגמישות חייב להיות גדול מ-PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS` או שווה לו.
ההשפעה של מגבלות על עבודה תקופתית
אפשר להחיל אילוצים על עבודה תקופתית. לדוגמה, אפשר להוסיף מגבלה לבקשת העבודה כך שהעבודה תתבצע רק כשהמכשיר של המשתמש בטעינה. במקרה כזה, גם אם חלף פרק הזמן המוגדר לחזרה, PeriodicWorkRequest לא יופעל עד שהתנאי הזה יתקיים. יכול להיות שריצה מסוימת של העבודה תתעכב, או אפילו תדלג אם התנאים לא יתקיימו בתוך מרווח הריצה.
מגבלות בעבודה
Constraints מוודאים שהעבודה נדחית עד שמתקיימים התנאים האופטימליים. אלה ההגבלות שזמינות ב-WorkManager:
| NetworkType | מגדיר את סוג הרשת שנדרש להפעלת העבודה.
לדוגמה, Wi-Fi (UNMETERED).
|
| BatteryNotLow | אם המדיניות מוגדרת כ-true, העבודה לא תפעל אם המכשיר במצב סוללה חלשה. |
| RequiresCharging | אם המדיניות מוגדרת כ-true, העבודה תופעל רק כשהמכשיר בטעינה. |
| DeviceIdle | אם המדיניות מוגדרת כ-True, המכשיר של המשתמש צריך להיות במצב לא פעיל כדי שהעבודה תתבצע. האפשרות הזו יכולה להיות שימושית להפעלת פעולות באצווה, שאחרת עלולות להשפיע לרעה על הביצועים של אפליקציות אחרות שפועלות באופן פעיל במכשיר של המשתמש. |
| StorageNotLow | אם המדיניות מוגדרת כ-true, העבודה לא תפעל אם נפח האחסון של המשתמש במכשיר נמוך מדי. |
כדי ליצור קבוצה של אילוצים ולשייך אותה לעבודה מסוימת, יוצרים מופע Constraints באמצעות Constraints.Builder() ומקצים אותו ל-WorkRequest.Builder().
לדוגמה, הקוד הבא יוצר בקשה לעבודה שמופעלת רק כשהמכשיר של המשתמש נטען ומחובר ל-Wi-Fi:
Kotlin
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val myWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
.setConstraints(constraints)
.build()
Java
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build();
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setConstraints(constraints)
.build();
אם מציינים כמה אילוצים, העבודה תופעל רק אם כל האילוצים יתקיימו.
אם מגבלה מסוימת לא תתקיים בזמן שהעבודה פועלת, WorkManager יפסיק את העובד. העבודה תנסה שוב כשיעמדו בכל האילוצים.
עבודות עם עיכוב
אם אין מגבלות על העבודה שלכם או שכל המגבלות מתקיימות כשהעבודה מתווספת לתור, יכול להיות שהמערכת תבחר להריץ את העבודה באופן מיידי. אם אתם לא רוצים שהעבודה תתבצע באופן מיידי, אתם יכולים לציין שהעבודה תתחיל אחרי השהיה ראשונית מינימלית.
הדוגמה הבאה ממחישה איך מגדירים את העבודה כך שהיא תפעל לפחות 10 דקות אחרי שהיא נוספה לתור.
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setInitialDelay(10, TimeUnit.MINUTES)
.build();
בדוגמה מוצג איך להגדיר השהיה ראשונית ל-OneTimeWorkRequest, אבל אפשר גם להגדיר השהיה ראשונית ל-PeriodicWorkRequest. במקרה כזה, רק ההפעלה הראשונה של העבודה התקופתית תתעכב.
מדיניות ניסיון חוזר והשהיה
אם רוצים ש-WorkManager ינסה שוב לבצע את העבודה, אפשר להחזיר Result.retry() מה-Worker. לאחר מכן, העבודה מתוזמנת מחדש בהתאם להשהיית נסיגה ולמדיניות נסיגה.
השהיה לפני ניסיון חוזר מציינת את משך הזמן המינימלי שצריך להמתין לפני שמנסים שוב לבצע את הפעולה אחרי הניסיון הראשון. הערך הזה לא יכול להיות קטן מ-10 שניות (או מ-MIN_BACKOFF_MILLIS).
מדיניות השהיה לפני ניסיון חוזר (backoff) מגדירה איך ההשהיה צריכה להתארך עם הזמן בניסיונות חוזרים. WorkManager תומך בשתי מדיניות השהיה לפני ניסיון חוזר (backoff),
LINEARו-EXPONENTIAL.
לכל בקשת עבודה יש מדיניות השהיה לפני ניסיון חוזר ועיכוב השהיה לפני ניסיון חוזר. מדיניות ברירת המחדל היא EXPONENTIAL עם עיכוב של 30 שניות, אבל אפשר לשנות את זה בהגדרות של בקשת העבודה.
דוגמה להתאמה אישית של מדיניות ושל השהיית הגיבוי:
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
בדוגמה הזו, ההשהיה המינימלית לפני ניסיון חוזר (backoff) מוגדרת לערך המינימלי המותר, 10 שניות. מכיוון שהמדיניות היא LINEAR, מרווח הניסיונות החוזרים יגדל בכ-10 שניות בכל ניסיון חדש. לדוגמה, אם הריצה הראשונה מסתיימת עם Result.retry(), המערכת תנסה שוב אחרי 10 שניות, ואם העבודה תמשיך להחזיר Result.retry() אחרי ניסיונות חוזרים, המערכת תנסה שוב אחרי 20 שניות, 30 שניות, 40 שניות וכן הלאה. אם מדיניות ההשהיה לפני ניסיון חוזר (backoff) הייתה מוגדרת לערך EXPONENTIAL, רצף משך הניסיון החוזר היה קרוב יותר ל-20, 40 ו-80.
תיוג עבודה
לכל בקשת עבודה יש מזהה ייחודי, שאפשר להשתמש בו כדי לזהות את העבודה בהמשך, במטרה לבטל אותה או לעקוב אחר ההתקדמות שלה.
אם יש לכם קבוצה של משימות שקשורות זו לזו באופן הגיוני, יכול להיות שיהיה לכם שימושי גם לתייג את המשימות האלה. תיוג מאפשר לכם לבצע פעולות על קבוצה של בקשות עבודה.
לדוגמה, הפקודה WorkManager.cancelAllWorkByTag(String) מבטלת את כל בקשות העבודה עם תג מסוים, והפקודה WorkManager.getWorkInfosByTag(String) מחזירה רשימה של אובייקטים מסוג WorkInfo שאפשר להשתמש בהם כדי לקבוע את מצב העבודה הנוכחי.
בדוגמה הבאה אפשר לראות איך מוסיפים תג 'ניקוי' לעבודה:
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.addTag("cleanup")
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.addTag("cleanup")
.build();
לבסוף, אפשר להוסיף כמה תגים לבקשת עבודה אחת. באופן פנימי, התגים האלה מאוחסנים כקבוצת מחרוזות. כדי לקבל את קבוצת התגים שמשויכים ל-WorkRequest, אפשר להשתמש ב-WorkInfo.getTags().
מתוך המחלקה Worker, אפשר לאחזר את מערך התגים שלה באמצעות ListenableWorker.getTags().
הקצאת נתוני קלט
יכול להיות שהמשימה תדרוש נתוני קלט כדי להתבצע. לדוגמה, כדי להעלות תמונה, יכול להיות שיהיה צורך ב-URI של התמונה כקלט.
ערכי הקלט מאוחסנים כצמדי מפתח/ערך באובייקט Data ואפשר להגדיר אותם בבקשת העבודה. WorkManager יעביר את הקלט Data לעבודה שלכם כשהיא תופעל. הכיתה Worker יכולה לגשת לארגומנטים של הקלט על ידי קריאה ל-Worker.getInputData(). בדוגמה הבאה אפשר לראות איך ליצור מופע של Worker שדורש נתוני קלט, ואיך לשלוח אותו בבקשת העבודה.
Kotlin
// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
: Worker(appContext, workerParams) {
override fun doWork(): Result {
val imageUriInput =
inputData.getString("IMAGE_URI") ?: return Result.failure()
uploadFile(imageUriInput)
return Result.success()
}
...
}
// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
.setInputData(workDataOf(
"IMAGE_URI" to "http://..."
))
.build()
Java
// Define the Worker requiring input
public class UploadWork extends Worker {
public UploadWork(Context appContext, WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public Result doWork() {
String imageUriInput = getInputData().getString("IMAGE_URI");
if(imageUriInput == null) {
return Result.failure();
}
uploadFile(imageUriInput);
return Result.success();
}
...
}
// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
new OneTimeWorkRequest.Builder(UploadWork.class)
.setInputData(
new Data.Builder()
.putString("IMAGE_URI", "http://...")
.build()
)
.build();
באופן דומה, אפשר להשתמש במחלקה Data כדי להציג ערך החזרה.
השלבים הבאים
בדף מצבים ותצפית מוסבר על מצבי עבודה ואיך לעקוב אחרי התקדמות העבודה.