במדריך לתחילת העבודה מוסבר איך ליצור 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 יכול לבצע עבודה חשובה, והמערכת מקבלת שליטה טובה יותר בגישה למשאבים.
מאפיינים של משימות בעדיפות גבוהה:
- חשיבות: עבודה מזורזת מתאימה למשימות שחשובות למשתמש או למשימות שהמשתמש יזם.
- מהירות: עבודה מהירה מתאימה למשימות קצרות שמתחילות מיד ומסתיימות תוך כמה דקות.
- מכסות: מכסה ברמת המערכת שמגבילה את זמן ההפעלה של תהליך בחזית, וקובעת אם אפשר להתחיל עבודה מזורזת.
- ניהול צריכת החשמל: הגבלות על ניהול צריכת החשמל, כמו חיסכון בסוללה ומצב שינה, לא צפויות להשפיע על עבודה מהירה.
- זמן אחזור: המערכת מבצעת באופן מיידי עבודה דחופה, בתנאי שעומס העבודה הנוכחי של המערכת מאפשר זאת. כלומר, הם רגישים לזמן אחזור ולא ניתן לתזמן אותם לביצוע מאוחר יותר.
מקרה שימוש פוטנציאלי לעבודה מזורזת יכול להיות באפליקציית צ'אט, כשהמשתמש רוצה לשלוח הודעה או תמונה מצורפת. באופן דומה, אפליקציה שמטפלת בתהליך תשלום או בתהליך הרשמה למינוי יכולה להשתמש גם היא בעבודה מזורזת. הסיבה לכך היא שהמשימות האלה חשובות למשתמש, הן מתבצעות במהירות ברקע, הן צריכות להתחיל באופן מיידי והן צריכות להמשיך להתבצע גם אם המשתמש סוגר את האפליקציה
מכסות
כדי שמשימה תפעל, המערכת צריכה להקצות לה זמן ביצוע. זמן הביצוע לא בלתי מוגבל. במקום זאת, כל אפליקציה מקבלת מכסת זמן ביצוע. כשהאפליקציה משתמשת בזמן הביצוע שלה ומגיעה למכסה שהוקצתה לה, אי אפשר יותר לבצע עבודה מזורזת עד שהמכסה מתעדכנת. כך מערכת 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 ומעלה, שירותים שפועלים בחזית עדיין זמינים לכם באמצעות ה-method המתאים setForeground
.
קובץ שירות
העובדים לא יודעים אם העבודה שהם מבצעים היא דחופה או לא. אבל בגרסאות מסוימות של Android, העובדים יכולים להציג התראה כשמזרזים WorkRequest
.
כדי להפעיל את האפשרות הזו, WorkManager מספק את ה-method getForegroundInfoAsync()
, שצריך להטמיע כדי ש-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).
מדיניות ההשהיה מגדירה איך ההשהיה צריכה להתארך עם הזמן בניסיונות חוזרים. WorkManager תומך ב-2 מדיניות נסיגה (backoff):
LINEAR
ו-EXPONENTIAL
.
לכל בקשת עבודה יש מדיניות השהיה לפני ניסיון חוזר (backoff) וזמן השהיה לפני ניסיון חוזר. מדיניות ברירת המחדל היא EXPONENTIAL
עם עיכוב של 30 שניות, אבל אפשר לשנות את זה בהגדרות של בקשת העבודה.
דוגמה להתאמה אישית של מדיניות ההשהיה והעיכוב:
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
בדוגמה הזו, ההשהיה המינימלית של הנסיגה מוגדרת לערך המינימלי המותר, 10 שניות. מכיוון שהמדיניות היא LINEAR
, מרווח הזמן בין הניסיונות יגדל בכ-10 שניות בכל ניסיון חדש. לדוגמה, אם הריצה הראשונה מסתיימת עם Result.retry()
, תתבצע ריצה חוזרת אחרי 10 שניות, ואם העבודה ממשיכה להחזיר Result.retry()
אחרי ניסיונות חוזרים, תתבצע ריצה חוזרת אחרי 20 שניות, אחרי 30 שניות, אחרי 40 שניות וכן הלאה. אם מדיניות ההשהיה הייתה מוגדרת ל-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
כדי להפיק ערך החזרה. מידע נוסף על נתוני הקלט והפלט מופיע בקטע פרמטרים של קלט וערכים שמוחזרים.
השלבים הבאים
בדף מצבים ותצפית מוסבר על מצבי עבודה ואיך לעקוב אחרי התקדמות העבודה.