ה-codelab הזה הוא חלק מהקורס Android Kotlin Fundamentals. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. כל ה-codelab של הקורס מפורטים בדף הנחיתה של ה-codelab בנושא יסודות Kotlin ל-Android.
מבוא
ברוב האפליקציות בעולם האמיתי יש צורך לבצע משימות ארוכות ברקע. לדוגמה, אפליקציה יכולה להעלות קבצים לשרת, לסנכרן נתונים משרת ולשמור אותם במסד נתונים Room, לשלוח יומנים לשרת או לבצע פעולות יקרות על נתונים. צריך לבצע פעולות כאלה ברקע, מחוץ ל-thread של ממשק המשתמש (ה-thread הראשי). משימות ברקע צורכות משאבים מוגבלים של המכשיר, כמו זיכרון RAM וסוללה. אם לא מטפלים בבעיה בצורה נכונה, היא עלולה לפגוע בחוויית המשתמש.
ב-Codelab הזה תלמדו איך להשתמש ב-WorkManager כדי לתזמן משימה שפועלת ברקע בצורה אופטימלית ויעילה. מידע נוסף על פתרונות אחרים לעיבוד ברקע ב-Android זמין במדריך לעיבוד ברקע.
מה שכדאי לדעת
- איך משתמשים ברכיבי הארכיטקטורה של Android ViewModel,LiveDataו-Room.
- איך מבצעים טרנספורמציות במחלקה LiveData.
- איך בונים ומפעילים קורוטינה.
- איך משתמשים במתאמי קישור בקישור נתונים.
- איך טוענים נתונים שנשמרו במטמון באמצעות תבנית מאגר.
מה תלמדו
- איך יוצרים Worker, שמייצג יחידת עבודה.
- איך יוצרים WorkRequestכדי לבקש ביצוע של עבודה.
- איך מוסיפים אילוצים ל-WorkRequestכדי להגדיר איך ומתי עובד צריך לפעול.
- איך משתמשים ב-WorkManagerכדי לתזמן משימות ברקע.
הפעולות שתבצעו:
- יוצרים Worker כדי להריץ משימה ברקע לאחזור מראש של פלייליסט הסרטונים DevBytes מהרשת.
- מתזמנים את העובד להפעלה תקופתית.
- הוספת מגבלות ל-WorkRequest.
- מתזמנים WorkRequestתקופתי שמופעל פעם ביום.
ב-codelab הזה תעבדו על אפליקציית DevBytes שפיתחתם ב-codelab קודם. (אם האפליקציה לא מותקנת במכשיר, אפשר להוריד קוד התחלתי לשיעור הזה).
באפליקציית DevBytes מוצגת רשימה של סרטוני DevByte, שהם מדריכים קצרים שנוצרו על ידי צוות יחסי המפתחים של Google Android. בסרטונים מוצגות תכונות למפתחים ושיטות מומלצות לפיתוח ל-Android.
כדי לשפר את חוויית המשתמש באפליקציה, אתם יכולים לבצע אחזור מראש של הסרטונים פעם ביום. כך המשתמשים מקבלים תוכן חדש ברגע שהם פותחים את האפליקציה.

במשימה הזו תורידו את קוד ההתחלה ותבדקו אותו.
שלב 1: הורדה והפעלה של אפליקציית המתחילים
אתם יכולים להמשיך לעבוד על אפליקציית DevBytes שיצרתם ב-codelab הקודם (אם יש לכם אותה). אפשרות נוספת היא להוריד את אפליקציית המתחילים.
במשימה הזו תורידו ותפעילו את אפליקציית המתחילים ותבדקו את קוד המתחילים.
- אם עדיין לא התקנתם את אפליקציית DevBytes, אתם יכולים להוריד את קוד ההתחלה של DevBytes בשביל ה-codelab הזה מפרויקט DevBytesRepository ב-GitHub.
- מחלצים את הקוד ופותחים את הפרויקט ב-Android Studio.
- אם מכשיר הבדיקה או האמולטור לא מחוברים לאינטרנט, צריך לחבר אותם. מבצעים Build לאפליקציה ומריצים אותה. האפליקציה מאחזרת את רשימת סרטוני DevByte מהרשת ומציגה אותם.
- באפליקציה, מקישים על סרטון כלשהו כדי לפתוח אותו באפליקציית YouTube.
שלב 2: בודקים את הקוד
אפליקציית המתחילים מגיעה עם הרבה קוד שהוצג ב-codelab הקודם. קוד ההתחלה של ה-codelab הזה כולל מודולים של רשת, ממשק משתמש, מטמון אופליין ומאגר. אפשר להתמקד בתזמון של משימת הרקע באמצעות WorkManager. 
- ב-Android Studio, מרחיבים את כל החבילות.
- מעיינים בחבילה database. החבילה מכילה את ישויות מסד הנתונים ואת מסד הנתונים המקומי, שמיושם באמצעותRoom.
- מעיינים בחבילה repository. החבילה מכילה את המחלקהVideosRepositoryשמבצעת הפשטה של שכבת הנתונים משאר האפליקציה.
- אתם יכולים לעיין בשאר קוד המתחילים בעצמכם, בעזרת ה-Codelab הקודם.
WorkManager הוא אחד מרכיבי הארכיטקטורה של Android וחלק מ-Android Jetpack. WorkManager מיועד לעבודות רקע שאפשר לדחות אותן ונדרשת הרצה מובטחת שלהן:
- ניתן לדחות – העבודה לא חייבת להתבצע באופן מיידי. לדוגמה, שליחת נתונים אנליטיים לשרת או סנכרון מסד הנתונים ברקע הם פעולות שאפשר לדחות.
- ביצוע מובטח אומר שהמשימה תפעל גם אם האפליקציה תיסגר או שהמכשיר יופעל מחדש.

בזמן ש-WorkManager פועל ברקע, הוא מטפל בבעיות תאימות ובשיטות מומלצות לשמירה על תקינות הסוללה והמערכת. WorkManager תואם לגרסאות עד רמת API 14. WorkManager בוחר דרך מתאימה לתזמון משימה ברקע, בהתאם לרמת ה-API של המכשיר. יכול להיות שהיא תשתמש ב-JobScheduler (ב-API 23 ומעלה) או בשילוב של AlarmManager ו-BroadcastReceiver.
WorkManager מאפשרת גם להגדיר קריטריונים להרצת המשימה ברקע. לדוגמה, יכול להיות שתרצו שהמשימה תפעל רק אם סטטוס הסוללה, סטטוס הרשת או סטטוס הטעינה עומדים בקריטריונים מסוימים. בהמשך ה-codelab הזה תלמדו איך להגדיר אילוצים.
ב-codelab הזה, מתזמנים משימה לאחזור מראש של פלייליסט הסרטונים DevBytes מהרשת פעם ביום. כדי לתזמן את המשימה הזו, משתמשים בספרייה WorkManager.
- פותחים את הקובץ build.gradle (Module:app)ומוסיפים את התלותWorkManagerלפרויקט.
 אם משתמשים בגרסה האחרונה של הספרייה, האפליקציה של הפתרון אמורה לעבור קומפילציה כמצופה. אם לא, צריך לפתור את הבעיה או לחזור לגרסת הספרייה שמוצגת בהמשך.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"- מסנכרנים את הפרויקט ומוודאים שאין שגיאות קומפילציה.
לפני שמוסיפים קוד לפרויקט, כדאי להכיר את המחלקות הבאות בספריית WorkManager:
- Worker
 במחלקת ה-class הזו מגדירים את העבודה בפועל (המשימה) שתופעל ברקע. אתם מרחיבים את המחלקה הזו ומבטלים את השיטהdoWork(). בשיטהdoWork()מוסיפים קוד שיופעל ברקע, כמו סנכרון נתונים עם השרת או עיבוד תמונות. במשימה הזו מטמיעים אתWorker.
- WorkRequest
 המחלקות האלה מייצגות בקשה להפעלת העובד ברקע. משתמשים ב-WorkRequestכדי להגדיר איך ומתי להריץ את משימת ה-Worker, בעזרתConstraintsכמו חיבור המכשיר לחשמל או חיבור ל-Wi-Fi. תטמיעו אתWorkRequestבמשימה מאוחרת יותר.
- WorkManager
 הכיתה הזו מתזמנת ומפעילה את- WorkRequest. - WorkManagerמתזמן בקשות עבודה באופן שמפזר את העומס על משאבי המערכת, תוך התחשבות במגבלות שציינתם. תטמיעו את- WorkManagerבמשימה מאוחרת יותר.
שלב 1: יצירת עובד
במשימה הזו, מוסיפים Worker כדי לבצע אחזור מראש של פלייליסט הסרטונים DevBytes ברקע.
- בתוך חבילת devbyteviewer, יוצרים חבילה חדשה בשםwork.
- בתוך חבילת work, יוצרים מחלקה חדשה של Kotlin בשםRefreshDataWorker.
- הרחבת השיעור RefreshDataWorkerמהשיעורCoroutineWorker. מעבירים אתcontextואתWorkerParametersכפרמטרים של בנאי.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}- כדי לפתור את השגיאה שקשורה למחלקה מופשטת, צריך לבטל את השיטה doWork()בתוך המחלקהRefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}פונקציית השהיה היא פונקציה שאפשר להשהות ולהמשיך אותה מאוחר יותר. פונקציה להשהיה יכולה להריץ פעולה ארוכה ולהמתין להשלמתה בלי לחסום את ה-thread הראשי.
שלב 2: הטמעה של doWork()
ה-method doWork() בתוך המחלקה Worker מופעל בשרשור ברקע. השיטה מבצעת עבודה באופן סינכרוני, והיא אמורה להחזיר אובייקט ListenableWorker.Result. מערכת Android נותנת ל-Worker עד 10 דקות לסיים את ההרצה ולהחזיר אובייקט ListenableWorker.Result. אחרי שפרק הזמן הזה מסתיים, המערכת מפסיקה את Worker באופן אוטומטי.
כדי ליצור אובייקט ListenableWorker.Result, קוראים לאחת מהשיטות הסטטיות הבאות כדי לציין את סטטוס השלמת העבודה ברקע:
- Result.success()– העבודה הושלמה בהצלחה.
- Result.failure()– העבודה הושלמה עם כשל קבוע.
- Result.retry()– העבודה נתקלה בשגיאה זמנית וצריך לנסות שוב.
במשימה הזו, מטמיעים את השיטה doWork() כדי לאחזר את פלייליסט הסרטונים DevBytes מהרשת. אפשר להשתמש מחדש בשיטות הקיימות במחלקה VideosRepository כדי לאחזר את הנתונים מהרשת.
- בכיתה RefreshDataWorker, בתוךdoWork(), יוצרים מופע של אובייקטVideosDatabaseומופע של אובייקטVideosRepository.
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)
   return Result.success()
}- בכיתה RefreshDataWorker, בתוךdoWork(), מעל ההצהרהreturn, קוראים למתודהrefreshVideos()בתוך בלוקtry. מוסיפים יומן כדי לעקוב אחרי מועד ההפעלה של ה-Worker.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}כדי לפתור את השגיאה 'הפניה לא פתורה', מייבאים את retrofit2.HttpException.
- לעיון, הנה הכיתה המלאה RefreshDataWorker:
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}Worker מגדיר יחידת עבודה, ו-WorkRequest מגדיר איך ומתי העבודה צריכה להתבצע. יש שתי הטמעות קונקרטיות של המחלקה WorkRequest: 
- הכיתה OneTimeWorkRequestמיועדת למטלות חד-פעמיות. (משימה חד-פעמית מתבצעת רק פעם אחת).
- הכיתה PeriodicWorkRequestמיועדת לעבודות תקופתיות, עבודות שחוזרות על עצמן במרווחי זמן קבועים.
המשימות יכולות להיות חד-פעמיות או מחזוריות, לכן חשוב לבחור את הכיתה בהתאם. מידע נוסף על תזמון עבודה חוזרת זמין במאמר בנושא עבודה חוזרת.
במשימה הזו מגדירים ומתזמנים WorkRequest להפעלת העובד שיצרתם במשימה הקודמת. 
שלב 1: הגדרת עבודה חוזרת
בתוך אפליקציית Android, המחלקה Application היא מחלקת הבסיס שמכילה את כל הרכיבים האחרים, כמו פעילויות ושירותים. כשנוצר התהליך של האפליקציה או החבילה, מתבצעת יצירה של מופע של המחלקה Application (או של כל מחלקת משנה של Application) לפני כל מחלקה אחרת.
באפליקציה לדוגמה הזו, המחלקה DevByteApplication היא מחלקת משנה של המחלקה Application. כדאי לתזמן את WorkManager בכיתה DevByteApplication.
- בכיתה DevByteApplication, יוצרים שיטה בשםsetupRecurringWork()כדי להגדיר את עבודת הרקע החוזרת.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}- בתוך השיטה setupRecurringWork(), יוצרים ומפעילים בקשה תקופתית לעבודה שתרוץ פעם ביום, באמצעות השיטהPeriodicWorkRequestBuilder(). מעבירים את המחלקהRefreshDataWorkerשיצרתם במשימה הקודמת. מעבירים אינטרוול חזרה של1עם יחידת זמן שלTimeUnit.DAYS.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()כדי לפתור את השגיאה, מייבאים את java.util.concurrent.TimeUnit.
שלב 2: תזמון WorkRequest באמצעות WorkManager
אחרי שמגדירים את WorkRequest, אפשר לתזמן אותו באמצעות WorkManager בשיטה enqueueUniquePeriodicWork(). השיטה הזו מאפשרת להוסיף PeriodicWorkRequest לתור עם שם ייחודי, כך שרק PeriodicWorkRequest אחד עם שם מסוים יכול להיות פעיל בכל פעם.
לדוגמה, יכול להיות שתרצו שרק פעולת סנכרון אחת תהיה פעילה. אם יש פעולת סנכרון אחת בהמתנה, אפשר לבחור להפעיל אותה או להחליף אותה בעבודה החדשה באמצעות ExistingPeriodicWorkPolicy.
מידע נוסף על דרכים לתזמון של WorkRequest זמין במאמר WorkManager.
- בכיתה RefreshDataWorker, בתחילת הכיתה, מוסיפים אובייקט נלווה. מגדירים שם עובד כדי לזהות באופן ייחודי את העובד הזה.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}- ב-class DevByteApplication, בסוף ה-methodsetupRecurringWork(), מתזמנים את העבודה באמצעות ה-methodenqueueUniquePeriodicWork(). מעבירים את הערך enumKEEPעבור ExistingPeriodicWorkPolicy. מעבירים את הערךrepeatingRequestכפרמטרPeriodicWorkRequest.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)אם יש עבודה בהמתנה (לא הושלמה) עם אותו שם, הפרמטר ExistingPeriodicWorkPolicy.KEEP גורם ל-WorkManager לשמור את העבודה התקופתית הקודמת ולבטל את בקשת העבודה החדשה.
- בתחילת המחלקה DevByteApplication, יוצרים אובייקטCoroutineScope. מעבירים אתDispatchers.Defaultכפרמטר של ה-constructor.
private val applicationScope = CoroutineScope(Dispatchers.Default)- במחלקה DevByteApplication, מוסיפים שיטה חדשה בשםdelayedInit()כדי להפעיל קורוטינה.
private fun delayedInit() {
   applicationScope.launch {
   }
}- בתוך הפונקציה delayedInit(), מפעילים את הפונקציהsetupRecurringWork().
- מעבירים את האתחול של Timber מה-method onCreate()ל-methoddelayedInit().
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}- במחלקה DevByteApplication, בסוף השיטהonCreate(), מוסיפים קריאה לשיטהdelayedInit().
override fun onCreate() {
   super.onCreate()
   delayedInit()
}- פותחים את החלונית Logcat בתחתית החלון של Android Studio. סינון לפי RefreshDataWorker.
- מריצים את האפליקציה. WorkManagerמתזמן את העבודה החוזרת באופן מיידי.
 בחלונית Logcat, שימו לב להצהרות היומן שמראות שהבקשה לעבודה מתוזמנת, ואז מופעלת בהצלחה.
D/RefreshDataWorker: Work request for sync is run I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
היומן WM-WorkerWrapper מוצג מהספרייה WorkManager, ולכן אי אפשר לשנות את הודעת היומן הזו.
שלב 3: (אופציונלי) תזמון של WorkRequest למרווח מינימלי
בשלב הזה, מקטינים את מרווח הזמן מיום אחד ל-15 דקות. הפעולה הזו מאפשרת לכם לראות את היומנים של בקשת עבודה תקופתית בפעולה.
- בקטע DevByteApplicationclass, בתוך השיטהsetupRecurringWork(), מוסיפים הערה להגדרה הנוכחית שלrepeatingRequest. הוספת בקשת עבודה חדשה עם מרווח חזרה תקופתי של15דקות.
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()- פותחים את החלונית Logcat ב-Android Studio ומסננים לפי RefreshDataWorker. כדי לנקות את היומנים הקודמים, לוחצים על הסמל Clear logcat . .
- מריצים את האפליקציה, ו-WorkManagerמתזמן את העבודה החוזרת באופן מיידי. בחלונית Logcat, אפשר לראות את היומנים – בקשת העבודה מופעלת פעם ב-15 דקות. צריך להמתין 15 דקות כדי לראות עוד קבוצה של יומני בקשות עבודה. אפשר להשאיר את האפליקציה פועלת או לסגור אותה. מנהל העבודה אמור להמשיך לפעול.
 שימו לב שהמרווח בין העלאות לפעמים קצר מ-15 דקות ולפעמים ארוך מ-15 דקות. (התזמון המדויק תלוי באופטימיזציות של הסוללה במערכת ההפעלה).
12:44:40 D/RefreshDataWorker: Work request for sync is run 12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 12:59:24 D/RefreshDataWorker: Work request for sync is run 12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 13:15:03 D/RefreshDataWorker: Work request for sync is run 13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 13:29:22 D/RefreshDataWorker: Work request for sync is run 13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 13:44:26 D/RefreshDataWorker: Work request for sync is run 13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 מעולה! יצרת עובד ותזמנת את בקשת העבודה עם WorkManager. אבל יש בעיה: לא ציינת אילוצים. WorkManager יתזמן את העבודה פעם ביום, גם אם הסוללה של המכשיר חלשה, המכשיר במצב שינה או שאין לו חיבור לרשת. הפעולה הזו תשפיע על הסוללה ועל הביצועים של המכשיר, ועלולה לפגוע בחוויית המשתמש.
במשימה הבאה, תוכלו לפתור את הבעיה הזו על ידי הוספת אילוצים.
במשימה הקודמת השתמשתם ב-WorkManager כדי לתזמן בקשת עבודה. במשימה הזו מוסיפים קריטריונים להחלטה מתי לבצע את העבודה.
כשמגדירים את WorkRequest, אפשר לציין אילוצים לגבי מתי Worker צריך לפעול. לדוגמה, יכול להיות שתרצו לציין שהעבודה תתבצע רק כשהמכשיר לא פעיל, או רק כשהמכשיר מחובר לחשמל ול-Wi-Fi. אפשר גם לציין מדיניות השהיה (backoff) לניסיון חוזר של עבודה. האילוצים הנתמכים הם שיטות ההגדרה ב-Constraints.Builder. מידע נוסף זמין במאמר בנושא הגדרת בקשות עבודה.
שלב 1: מוסיפים אובייקט Constraints ומגדירים אילוץ אחד
בשלב הזה יוצרים אובייקט Constraints ומגדירים לו אילוץ אחד – אילוץ מסוג רשת. (קל יותר לשים לב ליומנים כשיש רק אילוץ אחד. בשלב מאוחר יותר, תוכלו להוסיף עוד אילוצים).
- בכיתה DevByteApplication, בתחילתsetupRecurringWork(), מגדיריםvalמהסוגConstraints. משתמשים בשיטהConstraints.Builder().
val constraints = Constraints.Builder()כדי לפתור את השגיאה, מייבאים את androidx.work.Constraints.
- משתמשים ב-method setRequiredNetworkType()כדי להוסיף אילוץ מסוג רשת לאובייקטconstraints. משתמשים ב-enumUNMETEREDכדי שבקשת העבודה תפעל רק כשהמכשיר מחובר לרשת ללא הגבלת נפח.
.setRequiredNetworkType(NetworkType.UNMETERED)- משתמשים בשיטה build()כדי ליצור את האילוצים מהכלי ליצירת מבצעים.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()עכשיו צריך להגדיר את אובייקט Constraints שנוצר זה עתה לבקשת העבודה.
- בכיתה DevByteApplication, בתוך השיטהsetupRecurringWork(), מגדירים את האובייקטConstraintsכבקשת העבודה התקופתית,repeatingRequest. כדי להגדיר את האילוצים, מוסיפים את ה-methodsetConstraints()מעל הקריאה ל-methodbuild().
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()שלב 2: מריצים את האפליקציה ומסתכלים על היומנים
בשלב הזה, מריצים את האפליקציה ורואים את בקשת העבודה המוגבלת שמופעלת ברקע במרווחי זמן.
- כדי לבטל משימות שנקבעו מראש, צריך להסיר את האפליקציה מהמכשיר או מהאמולטור.
- פותחים את החלונית Logcat ב-Android Studio. בחלונית Logcat, מוחקים את היומנים הקודמים על ידי לחיצה על הסמל Clear logcat בצד ימין. סינון לפי בצד ימין. סינון לפיwork.
- משביתים את ה-Wi-Fi במכשיר או באמולטור, כדי לראות איך פועלות ההגבלות. הקוד הנוכחי מגדיר רק אילוץ אחד, שמציין שהבקשה צריכה לפעול רק ברשת ללא הגבלת נפח. מכיוון ש-Wi-Fi מושבת, המכשיר לא מחובר לרשת, בין אם היא מוגבלת או לא. לכן, המגבלה הזו לא תתקיים.
- מריצים את האפליקציה ומסתכלים על החלונית Logcat. המערכת WorkManagerמתזמנת את המשימה ברקע באופן מיידי. המשימה לא מופעלת כי היא לא עומדת בדרישות של הגבלת הרשת.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
- מפעילים את ה-Wi-Fi במכשיר או באמולטור וצופים בחלונית Logcat. עכשיו המשימה המתוזמנת ברקע מופעלת בערך כל 15 דקות, כל עוד מתקיימת מגבלת הרשת.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled 11:31:47 D/RefreshDataWorker: Work request for sync is run 11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 11:46:45 D/RefreshDataWorker: Work request for sync is run 11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:03:05 D/RefreshDataWorker: Work request for sync is run 12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:16:45 D/RefreshDataWorker: Work request for sync is run 12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:31:45 D/RefreshDataWorker: Work request for sync is run 12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 12:47:05 D/RefreshDataWorker: Work request for sync is run 12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 13:01:45 D/RefreshDataWorker: Work request for sync is run 13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
שלב 3: הוספת עוד אילוצים
בשלב הזה מוסיפים את האילוצים הבאים ל-PeriodicWorkRequest: 
- הסוללה לא חלשה.
- טעינת המכשיר.
- מצב המתנה של המכשיר; זמין רק ברמת API 23 (Android M) ומעלה.
מטמיעים את הפעולות הבאות במחלקה DevByteApplication.
- במחלקה DevByteApplication, בתוך השיטהsetupRecurringWork(), מציינים שבקשת העבודה תפעל רק אם הסוללה לא חלשה. מוסיפים את האילוץ לפני הקריאה לשיטהbuild()ומשתמשים בשיטהsetRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)- מעדכנים את בקשת העבודה כך שהיא תפעל רק כשהמכשיר בטעינה. מוסיפים את האילוץ לפני הקריאה לשיטה build()ומשתמשים בשיטהsetRequiresCharging().
.setRequiresCharging(true)- מעדכנים את בקשת העבודה כך שהיא תפעל רק כשהמכשיר לא פעיל. מוסיפים את האילוץ לפני הקריאה לשיטה build()ומשתמשים בשיטהsetRequiresDeviceIdle(). ההגבלה הזו מפעילה את בקשת העבודה רק כשהמשתמש לא משתמש במכשיר באופן פעיל. התכונה הזו זמינה רק ב-Android מגרסה 6.0  (Marshmallow) ואילך, לכן צריך להוסיף תנאי לגרסת SDKMומעלה.
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}הנה ההגדרה המלאה של אובייקט constraints.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()- בתוך השיטה setupRecurringWork(), משנים את מרווח הבקשות בחזרה לפעם ביום.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()הנה ההטמעה המלאה של השיטה setupRecurringWork(), עם יומן שמאפשר לעקוב אחרי התזמון של בקשת העבודה התקופתית. 
private fun setupRecurringWork() {
       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }- כדי להסיר את בקשת העבודה שנקבעה מראש, צריך להסיר את ההתקנה של אפליקציית DevBytes מהמכשיר או מהאמולטור.
- מריצים את האפליקציה, והבקשה לעבודה מתוזמנת מיד על ידי WorkManager. בקשת העבודה מופעלת פעם ביום, כשכל האילוצים מתקיימים.
- בקשת העבודה הזו תפעל ברקע כל עוד האפליקציה מותקנת, גם אם האפליקציה לא פועלת. לכן, כדאי להסיר את האפליקציה מהטלפון.
מעולה! הטמעתם ותזמנתם בקשת עבודה שחוסכת בסוללה לצורך אחזור מראש של סרטונים מדי יום באפליקציית DevBytes. WorkManager יתזמן ויפעיל את העבודה, ויבצע אופטימיזציה של משאבי המערכת. המשתמשים והסוללות שלהם יהיו מרוצים מאוד.
פרויקט Android Studio: DevBytesWorkManager.
- בעזרת WorkManagerAPI אפשר לתזמן בקלות משימות אסינכרוניות שניתנות לדחייה ושחייבים להריץ בצורה מהימנה.
- ברוב האפליקציות בעולם האמיתי יש צורך לבצע משימות ארוכות ברקע. כדי לתזמן משימה ברקע בצורה יעילה ואופטימלית, משתמשים ב-WorkManager.
- המחלקות העיקריות בספריית WorkManagerהןWorker,WorkRequestו-WorkManager.
- המחלקות Workerמייצגות יחידת עבודה. כדי להטמיע את משימת הרקע, מרחיבים את המחלקהWorkerומבטלים את ה-methoddoWork().
- המחלקות WorkRequestמייצגות בקשה לביצוע יחידת עבודה. WorkRequestהוא מחלקת הבסיס להגדרת פרמטרים לעבודה שמתזמנים ב-WorkManager.
- יש שתי הטמעות קונקרטיות של המחלקה WorkRequest: OneTimeWorkRequestלמשימות חד-פעמיות ו-PeriodicWorkRequestלבקשות עבודה תקופתיות.
- כשמגדירים את WorkRequest, אפשר לצייןConstraintsכדי לקבוע מתיWorkerצריך לפעול. האילוצים כוללים דברים כמו האם המכשיר מחובר לחשמל, האם המכשיר לא פעיל או האם הוא מחובר ל-Wi-Fi.
- כדי להוסיף אילוצים ל-WorkRequest, משתמשים בשיטות ההגדרה שמפורטות במסמכי התיעוד שלConstraints.Builder. לדוגמה, כדי לציין שהפונקציהWorkRequestלא תפעל אם הסוללה של המכשיר חלשה, משתמשים בשיטת ההגדרהsetRequiresBatteryNotLow().
- אחרי שמגדירים את WorkRequest, מעבירים את המשימה למערכת Android. כדי לעשות זאת, מתזמנים את המשימה באמצעות אחת מהשיטות שלWorkManagerenqueue.
- הזמן המדויק שבו מתבצעת הפעולה Workerתלוי באילוצים שמוגדרים ב-WorkRequestובאופטימיזציות של המערכת. WorkManagerנועד לספק את ההתנהגות הטובה ביותר האפשרית, בהתחשב בהגבלות האלה.
קורס ב-Udacity:
מסמכי תיעוד למפתחי Android:
אחר:
בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:
- אם צריך, מקצים שיעורי בית.
- להסביר לתלמידים איך להגיש מטלות.
- בודקים את שיעורי הבית.
אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.
אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.
שאלה 1
מהן ההטמעות הקונקרטיות של המחלקה WorkRequest? 
▢ OneTimeWorkPeriodicRequest
▢ OneTimeWorkRequest ו-PeriodicWorkRequest
▢ OneTimeWorkRequest ו-RecurringWorkRequest
▢ OneTimeOffWorkRequest ו-RecurringWorkRequest
שאלה 2
באיזו מהמחלקות הבאות משתמשת WorkManager כדי לתזמן את משימת הרקע ב-API מגרסה 23 ואילך? 
▢ רק JobScheduler
▢ BroadcastReceiver ו-AlarmManager
▢ AlarmManager ו-JobScheduler
▢ Scheduler ו-BroadcastReceiver
שאלה 3
איזה API משמש אותך כדי להוסיף אילוצים ל-WorkRequest? 
▢ setConstraints()
▢ addConstraints()
▢ setConstraint()
▢ addConstraintsToWorkRequest()
עוברים לשיעור הבא: 
קישורים ל-codelabs אחרים בקורס הזה מופיעים בדף הנחיתה של ה-codelabs בנושא יסודות Android Kotlin.