אחרי שמגדירים את 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. האפשרויות האלה פועלות כמו האפשרויות המקבילות להן ב-ExistingWorkPolicy.
התבוננות בעבודה שלכם
בכל שלב אחרי הוספת העבודה לתור, אפשר לבדוק את הסטטוס שלה על ידי שליחת שאילתה ל-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();
}
});
שאילתות מורכבות שקשורות לעבודה
גרסה WorkManager 2.4.0 ואילך תומכת בשאילתות מורכבות של עבודות שהוכנסו לתור באמצעות אובייקטים מסוג 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.
הפסקה של עובד פעיל
יש כמה סיבות לכך ש-WorkManager עשוי להפסיק את הפעולה Worker:
- ביקשת במפורש לבטל את המינוי (לדוגמה, התקשרת אל
WorkManager.cancelWorkById(UUID)). - במקרה של עבודה ייחודית, הוספתם במפורש לתור
WorkRequestחדש עםExistingWorkPolicyשלREPLACE. המינוי הישן ל-WorkRequestמבוטל באופן מיידי. - העבודה שלכם כבר לא עומדת במגבלות.
- המערכת הורתה לאפליקציה להפסיק את העבודה מסיבה כלשהי. זה יכול לקרות אם חורגים מהזמן הקצוב להפעלה של 10 דקות. המערכת תנסה שוב לבצע את הפעולה במועד מאוחר יותר.
במקרים כאלה, ה-Worker שלכם מופסק.
צריך להפסיק את כל העבודה שהייתה בתהליך ולשחרר את כל המשאבים שה-Worker מחזיק בהם. לדוגמה, בשלב הזה צריך לסגור את ה-handles הפתוחים למסדי נתונים ולקבצים. יש שני מנגנונים שבעזרתם אפשר להבין מתי ה-Worker מפסיק לפעול.
הקריאה החוזרת (callback) onStopped()
WorkManager מפעיל את ListenableWorker.onStopped() ברגע שה-Worker שלכם הופסק. אפשר לשנות את השיטה הזו כדי לסגור משאבים שאתם מחזיקים בהם.
המאפיין isStopped()
אפשר לקרוא לשיטה ListenableWorker.isStopped() כדי לבדוק אם ה-worker כבר הופסק. אם אתם מבצעים פעולות ארוכות או חוזרות ב-Worker, כדאי לבדוק את המאפיין הזה לעיתים קרובות ולהשתמש בו כאות להפסקת העבודה בהקדם האפשרי.
הערה: WorkManager מתעלם מההגדרה Result שהוגדרה על ידי Worker שקיבל את האות onStop, כי ה-Worker כבר נחשב למופסק.