אחרי שמגדירים את Worker
ואת WorkRequest
, השלב האחרון הוא להוסיף את העבודה לתור. הדרך הכי פשוטה להוסיף עבודה לתור היא לקרוא לשיטה enqueue()
של WorkManager ולהעביר את WorkRequest
שרוצים להריץ.
Kotlin
val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)
Java
WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);
חשוב להיזהר כשמוסיפים עבודה לתור כדי להימנע מכפילויות. לדוגמה, אפליקציה עשויה לנסות להעלות את היומנים שלה לשירות עורפי כל 24 שעות. אם לא תהיו זהירים, יכול להיות שתכניסו את אותה משימה לתור הרבה פעמים, למרות שהעבודה צריכה להתבצע רק פעם אחת. כדי להשיג את המטרה הזו, אפשר לתזמן את העבודה כעבודה ייחודית.
עבודה ייחודית
המושג 'עבודה ייחודית' הוא מושג חשוב שמבטיח שיהיה לכם רק מופע אחד של עבודה עם שם מסוים בכל זמן נתון. בניגוד למזהים, שמות ייחודיים הם קריאים לאנשים ומצוינים על ידי המפתח, במקום להיות נוצרים אוטומטית על ידי WorkManager. בניגוד לתגים, שמות ייחודיים משויכים רק למופע יחיד של עבודה.
אפשר להשתמש בעבודה ייחודית גם לעבודה חד-פעמית וגם לעבודה תקופתית. אתם יכולים ליצור רצף עבודה ייחודי באמצעות קריאה לאחת מהשיטות האלה, בהתאם לסוג העבודה שאתם מתכננים – עבודה חוזרת או עבודה חד-פעמית.
-
WorkManager.enqueueUniqueWork()
לעבודה חד-פעמית -
WorkManager.enqueueUniquePeriodicWork()
לעבודה תקופתית
שתי השיטות האלה מקבלות 3 ארגומנטים:
- uniqueWorkName –
String
שמשמש לזיהוי ייחודי של בקשת העבודה. - existingWorkPolicy –
enum
שמציין ל-WorkManager מה לעשות אם כבר יש שרשרת עבודה לא גמורה עם השם הייחודי הזה. מידע נוסף זמין במדיניות בנושא פתרון סכסוכים. - work –
WorkRequest
לתזמון.
באמצעות עבודה ייחודית, נוכל לפתור את הבעיה של תזמון כפול שציינו קודם.
Kotlin
val sendLogsWorkRequest =
PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest
)
Java
PeriodicWorkRequest sendLogsWorkRequest = new
PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
.setConstraints(new Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest);
עכשיו, אם הקוד מופעל בזמן שמשימת sendLogs כבר נמצאת בתור, המשימה הקיימת נשמרת ולא מתווספת משימה חדשה.
רצפים ייחודיים של פעולות יכולים להיות שימושיים גם אם אתם צריכים ליצור בהדרגה שרשרת ארוכה של משימות. לדוגמה, אפליקציה לעריכת תמונות עשויה לאפשר למשתמשים לבטל שרשרת ארוכה של פעולות. כל אחת מהפעולות האלה של ביטול השינוי עשויה להימשך זמן מה, אבל צריך לבצע אותן בסדר הנכון. במקרה כזה, האפליקציה יכולה ליצור שרשרת של פעולות ביטול ולהוסיף לשרשרת כל פעולת ביטול לפי הצורך. פרטים נוספים מופיעים במאמר בנושא שרשור עבודות.
מדיניות יישוב מחלוקות
כשמתזמנים עבודה ייחודית, צריך להגדיר ל-WorkManager איזו פעולה לבצע אם יש התנגשות. כדי לעשות את זה, מעבירים enum כשמוסיפים את העבודה לתור.
לעבודה חד-פעמית, אתם מספקים ExistingWorkPolicy
, שכולל 4 אפשרויות לטיפול בקונפליקט.
REPLACE
קיים לעבוד עם העבודה החדשה. האפשרות הזו מבטלת את העבודה הקיימת.KEEP
את העבודה הקיימת ומתעלם מהעבודה החדשה.APPEND
את העבודה החדשה בסוף העבודה הקיימת. המדיניות הזו תגרום לכך שהעבודה החדשה תקושר לעבודה הקיימת ותפעל אחרי שהעבודה הקיימת תסתיים.
העבודה הקיימת הופכת לדרישה מוקדמת לעבודה החדשה. אם העבודה הקיימת הופכת ל-CANCELLED
או ל-FAILED
, גם העבודה החדשה תהיה CANCELLED
או FAILED
.
אם רוצים שהעבודה החדשה תפעל בלי קשר לסטטוס של העבודה הקיימת,
צריך להשתמש ב-APPEND_OR_REPLACE
במקום זאת.
- הפונקציה
APPEND_OR_REPLACE
פועלת באופן דומה לפונקציהAPPEND
, אבל היא לא תלויה בסטטוס העבודה של דרישת הסף. אם העבודה הקיימת היאCANCELLED
אוFAILED
, העבודה החדשה עדיין תפעל.
כשמדובר בעבודה תקופתית, צריך לציין את הערך ExistingPeriodicWorkPolicy
, שכולל 2 אפשרויות: REPLACE
ו-KEEP
. האפשרויות האלה פועלות כמו האפשרויות המקבילות להן במדיניות הקיימת.
התבוננות בעבודה
בכל שלב אחרי הוספת עבודה לתור, אפשר לבדוק את הסטטוס שלה על ידי שליחת שאילתה ל-WorkManager לפי name
, id
או לפי tag
שמשויך אליה.
Kotlin
// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
Java
// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
השאילתה מחזירה ListenableFuture
של אובייקט WorkInfo
, שכולל את id
של העבודה, התגים שלה, State
הנוכחי שלה וכל מערך נתונים של פלט באמצעות Result.success(outputData)
.
הווריאציות LiveData
ו-Flow
של כל אחת מהשיטות מאפשרות לצפות בשינויים ב-WorkInfo
על ידי רישום של מאזין. לדוגמה, אם רוצים להציג הודעה למשתמש כשעבודה מסוימת מסתיימת בהצלחה, אפשר להגדיר את זה כך:
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect{ workInfo ->
if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show()
}
}
Java
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo.getState() != null &&
workInfo.getState() == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show();
}
});
שאילתות מורכבות שקשורות לעבודה
גרסה 2.4.0 ואילך של WorkManager תומכת בשאילתות מורכבות לגבי עבודות שהוכנסו לתור באמצעות אובייקטים של WorkQuery
. ב-WorkQuery אפשר לחפש עבודה לפי שילוב של התגים שלה, המצב והשם הייחודי שלה.
בדוגמה הבאה מוצג איך אפשר למצוא את כל העבודות עם התג syncTag שנמצאות במצב FAILED
או CANCELLED
, ושם העבודה הייחודי שלהן הוא preProcess או sync.
Kotlin
val workQuery = WorkQuery.Builder
.fromTags(listOf("syncTag"))
.addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(listOf("preProcess", "sync")
)
.build()
val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)
Java
WorkQuery workQuery = WorkQuery.Builder
.fromTags(Arrays.asList("syncTag"))
.addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(Arrays.asList("preProcess", "sync")
)
.build();
ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);
כל רכיב (תג, מצב או שם) ב-WorkQuery
מופרד מAND
הרכיבים האחרים. כל ערך ברכיב מופרד באמצעות OR
. לדוגמה: (name1 OR name2
OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)
.
WorkQuery
פועל גם עם המקבילה של LiveData, getWorkInfosLiveData()
, ועם המקבילה של Flow, getWorkInfosFlow()
.
ביטול והפסקת עבודה
אם אתם כבר לא צריכים שהעבודה שהוספתם לתור תרוץ, אתם יכולים לבקש לבטל אותה. אפשר לבטל עבודות על ידי name
, id
או על ידי tag
שמשויך אליהן.
Kotlin
// by id
workManager.cancelWorkById(syncWorker.id)
// by name
workManager.cancelUniqueWork("sync")
// by tag
workManager.cancelAllWorkByTag("syncTag")
Java
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
בפנים, WorkManager בודק את State
של העבודה. אם העבודה כבר הסתיימה, לא יקרה כלום. אחרת, סטטוס העבודה ישתנה לCANCELLED
והעבודה לא תופעל בעתיד. כל המשימות של WorkRequest
שתלויות בעבודה הזו גם CANCELLED
.
RUNNING
work
receives a call to
ListenableWorker.onStopped()
.
אפשר לבטל את השיטה הזו כדי לטפל בניקוי פוטנציאלי. מידע נוסף זמין במאמר בנושא עצירה של תהליך worker פעיל.
הפסקה של Worker פעיל
יש כמה סיבות לכך ש-WorkManager עשוי להפסיק את הפעולה Worker
:
- ביקשתם במפורש לבטל את המינוי (לדוגמה, התקשרתם אל
WorkManager.cancelWorkById(UUID)
). - במקרה של עבודה ייחודית,
הוספתם במפורש לתור
WorkRequest
חדש עםExistingWorkPolicy
שלREPLACE
. המינוי הישןWorkRequest
מבוטל באופן מיידי. - העבודה שלך כבר לא עומדת במגבלות.
- מסיבה כלשהי, המערכת הורתה לאפליקציה להפסיק את העבודה. זה יכול לקרות אם חורגים מהזמן הקצוב להרצה של 10 דקות. המערכת תנסה שוב לבצע את הפעולה מאוחר יותר.
במקרים כאלה, ה-Worker שלכם מופסק.
צריך להפסיק את כל העבודה שהייתה בתהליך ולשחרר את כל המשאבים שה-Worker מחזיק בהם. לדוגמה, בשלב הזה צריך לסגור את ה-handle הפתוח למסדי נתונים ולקבצים. יש שני מנגנונים שבעזרתם אפשר להבין מתי ה-Worker מפסיק לפעול.
הקריאה החוזרת (callback) onStopped()
WorkManager מפעיל את ListenableWorker.onStopped()
ברגע שה-Worker שלכם נעצר. אפשר לבטל את השיטה הזו כדי לסגור משאבים שאתם מחזיקים בהם.
המאפיין isStopped()
אפשר להתקשר לשיטה ListenableWorker.isStopped()
כדי לבדוק אם העובד כבר הופסק. אם אתם מבצעים פעולות ארוכות או חוזרות ב-Worker, כדאי לבדוק את המאפיין הזה לעיתים קרובות ולהשתמש בו כאות להפסקת העבודה בהקדם האפשרי.
הערה: WorkManager מתעלם מהערך Result
שמוגדר על ידי Worker שקיבל את האות onStop, כי ה-Worker כבר נחשב ל-Worker שהופסק.
התבוננות במצב של סיבת העצירה
כדי לנפות באגים ולגלות למה Worker
הופסק, אפשר לתעד את סיבת ההפסקה על ידי קריאה ל-WorkInfo.getStopReason()
:
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect { workInfo ->
if (workInfo != null) {
val stopReason = workInfo.stopReason
logStopReason(syncWorker.id, stopReason)
}
}
Java
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo != null) {
int stopReason = workInfo.getStopReason();
logStopReason(syncWorker.id, workInfo.getStopReason());
}
});