مدیریت کار

هنگامی که Worker و WorkRequest خود را تعریف کردید، آخرین مرحله این است که کار خود را در صف قرار دهید. ساده‌ترین راه برای صف‌بندی کار، فراخوانی متد WorkManager enqueue() است و WorkRequest که می‌خواهید اجرا کنید ارسال کنید.

کاتلین

val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

جاوا

WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

برای جلوگیری از تکراری شدن کار، احتیاط کنید. به عنوان مثال، یک برنامه ممکن است سعی کند گزارش های خود را هر 24 ساعت در یک سرویس پشتیبان آپلود کند. اگر مراقب نباشید، ممکن است در نهایت بارها یک کار را در نوبت قرار دهید، حتی اگر کار فقط یک بار اجرا شود. برای رسیدن به این هدف، می توانید کار را به عنوان یک کار منحصر به فرد برنامه ریزی کنید.

کار بی نظیر

کار منحصر به فرد یک مفهوم قدرتمند است که تضمین می کند که شما در هر زمان فقط یک نمونه از کار با نام خاصی دارید. برخلاف شناسه‌ها، نام‌های منحصربه‌فرد برای انسان قابل خواندن هستند و به‌جای اینکه به‌طور خودکار توسط WorkManager تولید شوند، توسط توسعه‌دهنده مشخص می‌شوند. برخلاف برچسب‌ها ، نام‌های منحصربه‌فرد تنها با یک نمونه کار مرتبط هستند.

کار منحصر به فرد را می توان برای کارهای یک بار و دوره ای اعمال کرد. بسته به اینکه در حال برنامه ریزی برای انجام کارهای تکراری یا یک بار کاری هستید، می توانید با فراخوانی یکی از این روش ها یک دنباله کاری منحصر به فرد ایجاد کنید.

هر دوی این روش ها 3 آرگومان را می پذیرند:

  • uniqueWorkName - String که برای شناسایی منحصر به فرد درخواست کار استفاده می شود.
  • existingWorkPolicy - enum که به WorkManager می‌گوید اگر در حال حاضر یک زنجیره کار ناتمام با آن نام منحصربه‌فرد وجود دارد، چه کاری انجام دهد. برای اطلاعات بیشتر به سیاست حل تعارض مراجعه کنید.
  • work - WorkRequest برای برنامه ریزی.

با استفاده از کار منحصربه‌فرد، می‌توانیم مشکل زمان‌بندی تکراری خود را که قبلاً ذکر شد برطرف کنیم.

کاتلین

val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

جاوا

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 مرتبط با آن بررسی کنید.

کاتلین

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

جاوا

// 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 از هر یک از روش ها به شما امکان می دهد تا با ثبت یک شنونده ، تغییرات در WorkInfo را مشاهده کنید . به عنوان مثال، اگر می خواهید زمانی که برخی از کارها با موفقیت به پایان رسید، پیامی را برای کاربر نمایش دهید، می توانید آن را به صورت زیر تنظیم کنید:

کاتلین

workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(), 
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}

جاوا

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 " است، پیدا کنید.

کاتلین

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)

جاوا

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 -ed است. به عنوان مثال: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...) .

WorkQuery همچنین با معادل LiveData، getWorkInfosLiveData() کار می کند.

لغو و توقف کار

اگر دیگر برای اجرا نیازی به کار قبلی خود ندارید، می‌توانید درخواست لغو آن را کنید. کار را می توان با name ، id یا tag مرتبط با آن لغو کرد.

کاتلین

// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

جاوا

// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

در زیر هود، WorkManager State کار را بررسی می کند. اگر کار از قبل تمام شده باشد، هیچ اتفاقی نمی افتد. در غیر این صورت وضعیت کار به CANCELLED تغییر می کند و کار در آینده اجرا نخواهد شد. هر شغل WorkRequest که به این کار وابسته باشد نیز CANCELLED خواهد شد.

در حال حاضر RUNNING یک تماس با ListenableWorker.onStopped() دریافت می کند. برای انجام هرگونه پاکسازی احتمالی، این روش را نادیده بگیرید. برای اطلاعات بیشتر به توقف کارگر در حال اجرا مراجعه کنید.

یک کارگر در حال اجرا را متوقف کنید

چند دلیل مختلف وجود دارد که اجرای Worker شما ممکن است توسط WorkManager متوقف شود:

  • شما صریحاً درخواست کردید که لغو شود (مثلاً با تماس با WorkManager.cancelWorkById(UUID) .
  • در مورد کار منحصربه‌فرد ، شما صریحاً یک WorkRequest جدید را با یک ExistingWorkPolicy of REPLACE قرار داده‌اید. WorkRequest قدیمی بلافاصله لغو شده در نظر گرفته می شود.
  • محدودیت های کار شما دیگر برآورده نمی شود.
  • سیستم به برنامه شما دستور داده است که کار شما را به دلایلی متوقف کند. اگر از مهلت اجرای 10 دقیقه تجاوز کنید، ممکن است این اتفاق بیفتد. کار برای امتحان مجدد در زمان بعدی برنامه ریزی شده است.

تحت این شرایط، Worker شما متوقف می شود.

شما باید با همکاری هر کاری را که در حال انجام بودید متوقف کنید و منابعی را که کارگر شما در اختیار دارد آزاد کنید. به عنوان مثال، در این مرحله باید دستگیره های باز را روی پایگاه داده ها و فایل ها ببندید. دو مکانیسم در اختیار شما وجود دارد تا متوجه شوید که کارگر شما در حال توقف است.

پاسخ به تماس onStoped().

WorkManager به محض اینکه Worker شما متوقف شد ListenableWorker.onStopped() را فراخوانی می کند. این روش را نادیده بگیرید تا هر منبعی را که ممکن است نگه داشته اید ببندید.

ویژگی isStoppped().

می توانید متد ListenableWorker.isStopped() را فراخوانی کنید تا بررسی کنید که آیا کارگر شما قبلاً متوقف شده است یا خیر. اگر عملیات طولانی مدت یا تکراری را در Worker خود انجام می دهید، باید این ویژگی را مرتباً بررسی کنید و از آن به عنوان سیگنالی برای توقف کار در اسرع وقت استفاده کنید.

توجه: WorkManager Result تنظیم شده توسط Worker که سیگنال onStop را دریافت کرده است را نادیده می گیرد، زیرا Worker قبلاً متوقف شده در نظر گرفته می شود.