הגדרה של בקשות עבודה

במדריך לתחילת העבודה מוסבר איך ליצור 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);

לעבודות מורכבות יותר אפשר להשתמש ב-builder:

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 לביצוע עבודה חשובה תוך הענקת שליטה טובה יותר למערכת יותר מדי גישה למשאבים.

עבודה מזורזת לא חשובה למאפיינים הבאים:

  • חשיבות: עבודה מזורזת מתאימה למשימות שחשובות למשתמש, או הם ביוזמת המשתמש.
  • מהירות: עבודה מזורזת מתאימה בצורה הטובה ביותר למשימות קצרות שמתחילות באופן מיידי. תוך כמה דקות.
  • Quotas: מכסה ברמת המערכת שמגבילה את זמן הביצוע בחזית. ההגדרה קובעת אם משימה מזורזת יכולה להתחיל.
  • ניהול צריכת חשמל: הגבלות על ניהול צריכת חשמל, כגון סוללה המצבים 'חיסכון' ו'נמנום' צפויים להשפיע פחות על עבודה מזורזת.
  • זמן אחזור: המערכת מבצעת באופן מיידי עבודה מזורזת, כל עוד עומס העבודה הנוכחי של המערכת מאפשר לה לעשות זאת. זה אומר שתהליך האחזור רגיש ולא ניתן לתזמן אותו לביצוע במועד מאוחר יותר.

תרחיש לדוגמה לעבודה מזורזת יכול להיות בתוך אפליקציית צ'אט כשהמשתמש רוצה לשלוח הודעה או תמונה מצורפת. באופן דומה, אפליקציה שמטפלת בתהליך תשלום או הרשמה ייתכן גם שתרצו להשתמש בעבודה מזורזת. הדבר מכיוון שהמשימות האלה חשובות למשתמש, מתבצעות במהירות צריכים להתחיל מייד, וצריך להמשיך לבצע אותן גם אם המשתמש סוגר את האפליקציה

מכסות

המערכת חייבת להקצות את זמן הביצוע למשימה מזורזת לפני שהיא יכולות לפעול. זמן הביצוע אינו מוגבל. במקום זאת, כל אפליקציה מקבלת מכסה של זמן הביצוע. כשהאפליקציה משתמשת בזמן הביצוע שלה ומגיע במכסה שהוקצתה, לא ניתן יותר לבצע עבודה מזורזת עד שמגיעים למכסה מתבצע רענון. כך מערכת Android יכולה לאזן בצורה יעילה יותר בין המשאבים השונים תרגום מכונה.

משך זמן הביצוע הזמין לאפליקציה מבוסס על Pendingby bucket (קטגוריית המתנה) והחשיבות של התהליך.

ניתן לקבוע מה קורה כאשר מכסת הביצוע לא מאפשרת משימה מזורזת שתופעל מיידית. יש פרטים נוספים בקטעי הקוד שבהמשך.

ביצוע עבודה מזורזת

החל מגרסה 2.7 של WorkManager, האפליקציה שלך יכולה לבצע קריאה אל setExpedited() כדי להצהיר על כך WorkRequest צריך לפעול מהר ככל האפשר תוך שימוש במשימה מזורזת. קטע הקוד הבא מדגים איך להשתמש ב-setExpedited():

Kotlin

val request = OneTimeWorkRequestBuilder<SyncWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

WorkManager.getInstance(context)
    .enqueue(request)

Java

OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
    .setInputData(inputData)
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build();

בדוגמה הזו, אנחנו מאתחלים מופע של OneTimeWorkRequest וקוראים setExpedited() על גבי התמונה. לאחר מכן הבקשה הזו הופכת לעבודה מזורזת. אם המכסה מאפשר, הוא יתחיל לפעול באופן מיידי ברקע. אם במכסה נעשה בו שימוש, הפרמטר OutOfQuotaPolicy מציין שהבקשה לפעול כרגיל ולא מזורזת.

תאימות לאחור ושירותים שפועלים בחזית

כדי לשמור על תאימות לאחור של משימות מזורזות, יכול להיות ש-WorkManager יפעיל שירות שפועל בחזית בגרסאות פלטפורמה שקודמות ל-Android 12. שירותים שפועלים בחזית יכולים להציג התראה למשתמש.

השיטות getForegroundInfoAsync() ו-getForegroundInfo() ב-Worker הפעלה של WorkManager להצגת התראה כשתתבצע שיחה אל setExpedited() שקודמים ל-Android 12.

כל ListenableWorker חייב להטמיע את ה-method getForegroundInfo אם רוצה לבקש שהמשימה תפעל כמשימה מזורזת.

כשמטרגטים ל-Android מגרסה 12 ואילך, שירותים שפועלים בחזית עדיין זמינים באמצעות שיטת הsetForeground המתאימה.

עובד

העובדים לא יודעים אם העבודה שלהם מזורזת או לא. אבל עובדים יכולים להציג התראה בגרסאות מסוימות של Android כאשר הבקשה של WorkRequest הופעלה במהירות.

כדי להפעיל זאת, WorkManager מספק את ה-method getForegroundInfoAsync(), שעליך ליישם כדי ש-WorkManager יוכל להציג התראה ForegroundService עבורך במקרה הצורך.

עובדי Coroutine

אם משתמשים ב-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, וכתוצאה מכך הבקשה תבוטל אם אין מכסה מספקת.

אפליקציה לדוגמה

כדי לראות דוגמה מלאה לאופן שבו WorkManager 2.7.0 משתמש בעבודה מזורזת: דרך WorkManagerSample ב-GitHub.

עבודה מזורזת שנדחתה

המערכת מנסה לבצע משימה מזורזת נתונה בהקדם האפשרי לאחר מופעלת. עם זאת, כמו בסוגי עבודה אחרים, המערכת עשוי לדחות את ההתחלה של עבודה מזורזת חדשה, למשל במקרים הבאים:

  • עומס: עומס המערכת גבוה מדי, מה שעלול לקרות כשיש יותר מדי משימות פועלת, או כשאין מספיק זיכרון במערכת.
  • Quota (מכסה): חרגתם ממכסת המשימות המואצת. עבודה מזורזת משתמשת במערכת מכסות המבוססת על קטגוריות המתנה של אפליקציה, ומגבילה את הזמן המקסימלי לביצוע בחלון זמן נע. המכסות שמשמשות עבודה מזורזת מגבילה יותר מעבודה מסוגים אחרים עבודות ברקע.

תזמון עבודות תקופתיות

ייתכן שלפעמים האפליקציה תדרוש שעבודות מסוימות יפעלו מדי פעם. לדוגמה, מומלץ לגבות מדי פעם את הנתונים, להוריד תוכן חדש או מעלים יומנים לשרת.

כך משתמשים ב-PeriodicWorkRequest כדי ליצור אובייקט WorkRequest שמופעל מדי פעם:

Kotlin

val saveRequest =
       PeriodicWorkRequestBuilderS<aveImageToFileWorker(>1, TimeUnit.HOURS)
    // Additional configuration
           .build()

Java

PeriodicWorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
           // Constraints
           .build();

בדוגמה הזו, העבודה מתוזמנת למרווח של שעה.

תקופת המרווח מוגדרת כזמן המינימלי בין חזרות. הזמן המדויק שבו העובד יבצע תלוי במגבלות שבו אתם משתמשים באובייקט WorkRequest ובאופטימיזציות שבוצעו על ידי המערכת.

מרווחי הרצה גמישים

אם אופי העבודה שלך רגיש לתזמון ההרצה שלך, אפשר להגדיר PeriodicWorkRequest לרוץ במסגרת גמישות בתוך כל פרק זמן, כפי שמוצג באיור 1.

אתם יכולים להגדיר פרק זמן גמיש למשימה תקופתית. אתם מגדירים מרווח זמן לחזרה,
ואת פרק הזמן הגמיש שמציין פרק זמן מסוים
של הפסקה בין חזרה על מילים. WorkManager מנסה להפעיל את העבודה שלך בשלב כלשהו במהלך
פרק זמן גמיש בכל מחזור.

איור 1. התרשים מציג מרווחים חוזרים עם התקופה הגמישה שהעבודה יכולה להריץ.

כדי להגדיר עבודה תקופתית עם תקופה גמישה, מעבירים flexInterval יחד עם repeatInterval במהלך היצירה של PeriodicWorkRequest. התקופה הגמישה מתחיל ב-repeatInterval - flexInterval ונמשך עד סוף המרווח.

הדוגמה הבאה היא של עבודות תקופתיות שיכולות לפעול במהלך 15 השעות האחרונות דקות בכל פרק זמן של שעה.

Kotlin

val myUploadWork = PeriodicWorkRequestBuilderS<aveImageToFileWorker(>
       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 לא יפעל עד שתנאי זה מתקיים. הפעולה הזו יכולה תגרום לעיכוב של ריצה מסוימת בעבודה, או אפילו לדלג עליה, לא מתקיימים התנאים בפרק הזמן של הריצה.

מגבלות עבודה

המגבלות מונעות עבודה עד שהתנאים האופטימליים מתקיימים. האילוצים הבאים זמינים ל-WorkManager.

סוג הרשת מגבילה את סוג הרשת שנדרש כדי שהעבודה תפעל. לדוגמה, Wi-Fi (UNMETERED).
סוללה לא נמוכה אם המדיניות מוגדרת כ-True, העבודה לא תפעל כשהמכשיר נמצא במצב סוללה חלשה.
נדרשת טעינה אם המדיניות מוגדרת כ-True, העבודה תפעל רק כשהמכשיר בטעינה.
DeviceIdle אם המדיניות מוגדרת כ-True, המכשיר של המשתמש לא יהיה פעיל כדי שהעבודה תתבצע. האפשרות הזו שימושית להרצת פעולות באצווה, שאחרת עלולות להיות להן השפעה שלילית על הביצועים באפליקציות אחרות שפועלות באופן פעיל במכשיר של המשתמש.
StorageNotLow אם המדיניות מוגדרת כ-True, העבודה לא תפעל אם נפח האחסון של המשתמש במכשיר נמוך מדי.

כדי ליצור קבוצת אילוצים ולשייך אותה לעבודה מסוימת, את המכונה Constraints באמצעות Contraints.Builder() ומקצים אותה WorkRequest.Builder().

לדוגמה, הקוד הבא בונה בקשת עבודה שרצה רק כאשר שהמכשיר של המשתמש בטעינה וגם מחובר ל-Wi-Fi:

Kotlin

val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()

val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilderM<yWork(>)
       .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 = OneTimeWorkRequestBuilderM<yWork(>)
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

Java

WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

אמנם הדוגמה ממחישה איך להגדיר עיכוב ראשוני OneTimeWorkRequest, אפשר גם להגדיר השהיה ראשונית של PeriodicWorkRequest. במקרה הזה, רק ההפעלה הראשונה של העבודה התקופתית יהיה עיכוב.

המדיניות בנושא ניסיון חוזר והשהיה לפני ניסיון חוזר (backoff)

אם דורשים ש-WorkManager יתבצע ניסיון חוזר לעבודה, אפשר להחזיר Result.retry() מהעובד שלך. אחרי שתעשו את זה, נקבע מועד חדש בהתאם לעיכוב לפני ניסיון חוזר ולמדיניות בנושא השהיה לפני ניסיון חוזר.

  • השהיה לפני ניסיון חוזר מציינת את משך הזמן המינימלי שצריך להמתין לפני ניסיון חוזר את העבודה אחרי הניסיון הראשון. הערך הזה יכול להיות באורך 10 שניות לכל היותר (או MIN_BACKOFF_MILLIS).

  • מדיניות ההשהיה לפני ניסיון חוזר מגדירה איך עיכוב ההשהיה לפני ניסיון חוזר (backoff) צריך להימשך לאורך זמן ניסיונות חוזרים נוספים. ב-WorkManager יש 2 כללי מדיניות להשהיה לפני ניסיון חוזר (backoff) LINEAR והקבוצה EXPONENTIAL.

לכל בקשת עבודה יש מדיניות השהיה לפני ניסיון חוזר ועיכוב לפני ניסיון חוזר (backoff). מדיניות ברירת המחדל EXPONENTIAL עם עיכוב של 30 שניות, אבל ניתן לעקוף זאת להגדרת בקשות עבודה.

בהמשך מוצגת דוגמה להתאמה אישית של המדיניות והשהיית ההשהיה לפני ניסיון חוזר.

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilderM<yWork(>)
   .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();

בדוגמה הזו, ההשהיה המינימלית של השהיה לפני ניסיון חוזר (backoff) מוגדרת לערך המינימלי המותר, 10 שניות. מכיוון שהמדיניות היא LINEAR, מרווח הזמן החוזר יגדל ב- כ-10 שניות בכל ניסיון חדש. לדוגמה, ההפעלה הראשונה יתבצע ניסיון סיום עם Result.retry() שוב אחרי 10 שניות, ואחריה 20, 30, 40 וכן הלאה, אם היצירה ממשיכה Result.retry() לאחר הניסיונות הבאים. אם מדיניות ההשהיה לפני ניסיון חוזר (backoff) הוגדרה EXPONENTIAL, הרצף של משך הניסיונות החוזרים יהיה קרוב יותר ל-20, 40, 80, כך מופעלת.

תיוג עבודה

לכל בקשת עבודה יש מזהה ייחודי, שיכול לשמש כדי לזהות שהוגדרו מאוחר יותר כדי לבטל את העבודה או לצפות בהתקדמות.

אם יש לכם קבוצה של עבודות שקשורות באופן לוגי, אולי תוכלו לעזור לתייג את פריטי העבודה האלה. התיוג מאפשר לך לפעול בקבוצת עבודה יחד.

לדוגמה, WorkManager.cancelAllWorkByTag(String) מבטל את כל בקשות העבודה עם תג מסוים, וגם הפונקציה WorkManager.getWorkInfosByTag(String) מחזירה רשימה של אובייקטים של WorkInfo שבהם אפשר להשתמש כדי לקבוע את מצב העבודה הנוכחי.

הקוד הבא מראה איך מוסיפים 'ניקוי' תג ליצירה שלך:

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilderM<yWork(>)
   .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 = OneTimeWorkRequestBuilderU<ploadWork(>)
   .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 כדי ליצור פלט של ערך מוחזר. קלט ו מידע מפורט יותר על נתוני הפלט בסעיף פרמטרים של קלט מוחזרים ערכים.

השלבים הבאים

בדף מדינות ותצפית מופיע מידע נוסף על מצבי עבודה ואיך לעקוב אחר התקדמות העבודה שלכם.