W przewodniku dla początkujących opisaliśmy, jak utworzyć zadanie WorkRequest i dodać je do kolejki.
Z tego przewodnika dowiesz się, jak definiować i dostosowywać obiekty WorkRequest, aby obsługiwać typowe przypadki użycia, takie jak:
- Planowanie jednorazowych i cyklicznych zadań
- Ustawianie ograniczeń dotyczących pracy, takich jak wymaganie połączenia z Wi-Fi lub ładowania
- Gwarancja minimalnego opóźnienia w wykonywaniu zadań
- Ustawianie strategii ponawiania i wycofywania
- Przekazywanie danych wejściowych do zadania
- Grupowanie powiązanych zadań za pomocą tagów
Przegląd
Praca jest definiowana w WorkManagerze za pomocą klasy WorkRequest. Aby zaplanować zadanie za pomocą WorkManagera, musisz najpierw utworzyć obiekt WorkRequest, a następnie dodać go do kolejki.
Kotlin
val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)
Java
WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);
Obiekt WorkRequest zawiera wszystkie informacje potrzebne WorkManagerowi do planowania i wykonywania zadań. Obejmuje ograniczenia, które muszą zostać spełnione, aby zadanie mogło zostać wykonane, informacje o harmonogramie, takie jak opóźnienia lub powtarzające się interwały, konfigurację ponownych prób, a także dane wejściowe, jeśli zadanie ich wymaga.
WorkRequest to abstrakcyjna klasa bazowa. Istnieją 2 pochodne implementacje tej klasy, których możesz użyć do utworzenia żądania: OneTimeWorkRequest i PeriodicWorkRequest.
Jak sugerują nazwy, OneTimeWorkRequest jest przydatny do planowania zadań, które się nie powtarzają, a PeriodicWorkRequest lepiej nadaje się do planowania zadań, które powtarzają się w określonych odstępach czasu.
Planowanie jednorazowego zadania
W przypadku podstawowych zadań, które nie wymagają dodatkowej konfiguracji, użyj metody statycznej from:
Kotlin
val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
Java
WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);
W przypadku bardziej złożonych zadań możesz użyć kreatora:
Kotlin
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
// Additional configuration
.build()
Java
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
// Additional configuration
.build();
Planowanie prac priorytetowych
W bibliotece WorkManager 2.7.0 wprowadziliśmy koncepcję prac priorytetowych. Dzięki temu WorkManager może wykonywać ważne zadania, a system ma lepszą kontrolę nad dostępem do zasobów.
Prace priorytetowe wyróżniają się tymi cechami:
- Ważność: prace priorytetowe są odpowiednie do zadań, które są ważne dla użytkownika lub są przez niego inicjowane.
- Szybkość: prace priorytetowe najlepiej sprawdzają się w przypadku krótkich zadań, które rozpoczynają się natychmiast i kończą w ciągu kilku minut.
- Limity: limit na poziomie systemu, który ogranicza czas wykonywania na pierwszym planie, określa, czy zadanie priorytetowe może się rozpocząć.
- Zarządzanie zasilaniem: ograniczenia zarządzania zasilaniem, takie jak oszczędzanie baterii i tryb uśpienia, rzadziej wpływają na prace priorytetowe.
- Czas oczekiwania: system natychmiast wykonuje prace priorytetowe, o ile pozwala na to jego bieżące obciążenie. Oznacza to, że są one wrażliwe na czas oczekiwania i nie można ich zaplanować do późniejszego wykonania.
Potencjalnym przypadkiem użycia prac priorytetowych może być aplikacja do obsługi czatu, gdy użytkownik chce wysłać wiadomość lub załączony obraz. Podobnie aplikacja, która obsługuje proces płatności lub subskrypcji, może również korzystać z prac priorytetowych. Wynika to z tego, że te zadania są ważne dla użytkownika, szybko wykonują się w tle, muszą rozpocząć się natychmiast i powinny być kontynuowane nawet wtedy, gdy użytkownik zamknie aplikację.
Limity
Zanim zadanie priorytetowe będzie mogło zostać uruchomione, system musi przydzielić mu czas wykonania. Czas wykonywania nie jest nieograniczony. Zamiast tego każda aplikacja otrzymuje limit czasu wykonywania. Gdy aplikacja wykorzysta czas wykonywania i osiągnie przydzielony limit, nie będzie można wykonywać prac priorytetowych, dopóki limit nie zostanie odświeżony. Dzięki temu Android może skuteczniej równoważyć zasoby między aplikacjami.
Czas wykonywania dostępny dla aplikacji zależy od zasobnika gotowości i ważności procesu.
Możesz określić, co się stanie, gdy limit wykonywania nie pozwoli na natychmiastowe uruchomienie zadania priorytetowego. Szczegółowe informacje znajdziesz we fragmentach poniżej.
Wykonywanie prac priorytetowych
Od wersji WorkManager 2.7 aplikacja może wywoływać metodę setExpedited(), aby zadeklarować, że obiekt WorkRequest powinien zostać uruchomiony jak najszybciej jako zadanie priorytetowe. Ten fragment kodu pokazuje, jak używać setExpedited():
Kotlin
val request = OneTimeWorkRequestBuilder<SyncWorker>()
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build()
WorkManager.getInstance(context)
.enqueue(request)
Java
OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
.setInputData(inputData)
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build();
W tym przykładzie inicjujemy instancję OneTimeWorkRequest i wywołujemy na niej setExpedited(). To żądanie staje się wtedy pracą priorytetową. Jeśli limit na to pozwala, rozpocznie się natychmiast w tle. Jeśli limit został wykorzystany, parametr OutOfQuotaPolicy wskazuje, że żądanie powinno zostać uruchomione jako normalne, niepriorytetowe zadanie.
Zgodność wsteczna i usługi działające na pierwszym planie
Aby zachować zgodność wsteczną w przypadku zadań priorytetowych, WorkManager może uruchamiać usługę na pierwszym planie na platformach w wersjach starszych niż Android 12. Usługi działające na pierwszym planie mogą wyświetlać użytkownikowi powiadomienia.
Metody getForegroundInfoAsync() i getForegroundInfo() w klasie Worker umożliwiają WorkManagerowi wyświetlanie powiadomienia, gdy wywołujesz funkcję setExpedited() w systemie Android 12 lub starszym.
Jeśli chcesz, aby zadanie było wykonywane jako zadanie priorytetowe, każda klasa ListenableWorker musi implementować metodę getForegroundInfo.
W przypadku kierowania na Androida 12 lub nowszego usługi na pierwszym planie pozostają dostępne za pomocą odpowiedniej metody setForeground.
Instancja robocza
Pracownicy nie wiedzą, czy wykonywana przez nich praca jest przyspieszona. Jednak w niektórych wersjach Androida pracownicy mogą wyświetlać powiadomienie, gdy WorkRequest zostanie przyspieszony.
Aby to umożliwić, WorkManager udostępnia metodę getForegroundInfoAsync(), którą musisz zaimplementować, aby WorkManager mógł wyświetlić powiadomienie o rozpoczęciu ForegroundService, gdy będzie to konieczne.
CoroutineWorker
Jeśli używasz CoroutineWorker, musisz wdrożyć getForegroundInfo(). Następnie przekazujesz go do setForeground() w doWork(). Spowoduje to utworzenie powiadomienia w wersjach Androida starszych niż 12.
Przyjrzyj się temu przykładowi:
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()
}
}
Zasady dotyczące limitów
Możesz kontrolować, co dzieje się z pracami priorytetowymi, gdy aplikacja osiągnie limit wykonywania. Aby kontynuować, możesz przekazać setExpedited():
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, co powoduje, że zadanie jest uruchamiane jako zwykłe żądanie pracy. Widać to we wcześniejszym fragmencie kodu.OutOfQuotaPolicy.DROP_WORK_REQUEST, co powoduje anulowanie żądania, jeśli nie ma wystarczającego limitu.
Odroczone prace priorytetowe
System próbuje wykonać dane zadanie priorytetowe jak najszybciej po jego wywołaniu. Jednak podobnie jak w przypadku innych typów zadań system może opóźnić rozpoczęcie nowych prac priorytetowych, na przykład w tych przypadkach:
- Obciążenie: obciążenie systemu jest zbyt duże, co może się zdarzyć, gdy jest już uruchomionych zbyt wiele zadań lub gdy system nie ma wystarczającej ilości pamięci.
- Quota: przekroczono limit zadań priorytetowych. Prace priorytetowe korzystają z systemu limitów opartego na zasobnikach czuwania aplikacji i ograniczają maksymalny czas wykonywania w ramach ruchomego okna czasowego. Limity dotyczące prac priorytetowych są bardziej restrykcyjne niż te, które dotyczą innych typów zadań w tle.
Planowanie okresowych zadań
Aplikacja może czasami wymagać okresowego wykonywania określonych zadań. Możesz na przykład okresowo tworzyć kopie zapasowe danych, pobierać nowe treści w aplikacji lub przesyłać logi na serwer.
Oto jak użyć PeriodicWorkRequest do utworzenia obiektu WorkRequest, który jest wykonywany okresowo:
Kotlin
val saveRequest =
PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
// Additional configuration
.build()
Java
PeriodicWorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
// Constraints
.build();
W tym przykładzie zadanie jest zaplanowane z godzinnym interwałem.
Interwał jest definiowany jako minimalny czas między powtórzeniami. Dokładny czas wykonania instancji roboczej zależy od ograniczeń używanych w obiekcie WorkRequest i optymalizacji przeprowadzanych przez system.
Elastyczne interwały biegowe
Jeśli charakter Twojej pracy sprawia, że czas wykonywania zadania jest istotny, możesz skonfigurować PeriodicWorkRequest tak, aby działał w okresie elastycznym w każdym interwale, jak pokazano na rysunku 1.

Rysunek 1. Diagram przedstawia powtarzające się interwały z elastycznym okresem, w którym zadanie może zostać wykonane.
Aby zdefiniować zadanie okresowe z elastycznym okresem, podczas tworzenia obiektu PeriodicWorkRequest przekaż flexInterval wraz z repeatInterval. Okres elastyczny zaczyna się o repeatInterval - flexInterval i trwa do końca przedziału czasu.
Poniżej znajduje się przykład zadania okresowego, które może być wykonywane w ciągu ostatnich 15 minut każdej godziny.
Kotlin
val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
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();
Interwał powtarzania musi być większy lub równy PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, a interwał elastyczny musi być większy lub równy PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS`.
Wpływ ograniczeń na zadania okresowe
Do zadań okresowych możesz stosować ograniczenia. Możesz na przykład dodać do żądania zadania ograniczenie, zgodnie z którym zadanie będzie wykonywane tylko wtedy, gdy urządzenie użytkownika będzie się ładować. W takim przypadku, nawet jeśli upłynie określony interwał powtarzania, PeriodicWorkRequest nie zostanie uruchomiony, dopóki ten warunek nie zostanie spełniony. Może to spowodować opóźnienie konkretnego uruchomienia zadania, a nawet jego pominięcie, jeśli warunki nie zostaną spełnione w przedziale czasu uruchomienia.
Ograniczenia dotyczące pracy
Constraints dba o to, aby praca była odkładana do czasu spełnienia optymalnych warunków. W WorkManagerze dostępne są te ograniczenia:
| NetworkType | Ogranicza typ sieci wymagany do wykonania zadania.
Na przykład Wi-Fi (UNMETERED).
|
| BatteryNotLow | Jeśli ustawisz wartość „true”, zadanie nie zostanie uruchomione, gdy urządzenie będzie w trybie niskiego poziomu baterii. |
| RequiresCharging | Jeśli ustawisz wartość „true”, zadanie będzie wykonywane tylko wtedy, gdy urządzenie będzie się ładować. |
| DeviceIdle | Jeśli ma wartość Prawda, urządzenie użytkownika musi być nieaktywne, aby zadanie mogło zostać wykonane. Może to być przydatne do wykonywania operacji wsadowych, które w przeciwnym razie mogłyby mieć negatywny wpływ na wydajność innych aplikacji działających aktywnie na urządzeniu użytkownika. |
| StorageNotLow | Jeśli ustawisz wartość „true”, zadanie nie zostanie wykonane, gdy użytkownik będzie miał zbyt mało miejsca na dane na urządzeniu. |
Aby utworzyć zestaw ograniczeń i powiązać go z zadaniem, utwórz instancję Constraints za pomocą Constraints.Builder() i przypisz ją do WorkRequest.Builder().
Na przykład ten kod tworzy żądanie, które jest wykonywane tylko wtedy, gdy urządzenie użytkownika jest ładowane i połączone z Wi-Fi:
Kotlin
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val myWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
.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();
Jeśli określisz kilka ograniczeń, zadanie zostanie uruchomione tylko wtedy, gdy wszystkie zostaną spełnione.
Jeśli podczas wykonywania zadania ograniczenie przestanie być spełniane, WorkManager zatrzyma instancję roboczą. Zadanie zostanie ponownie uruchomione, gdy wszystkie ograniczenia zostaną spełnione.
Delayed Work
Jeśli zadanie nie ma żadnych ograniczeń lub wszystkie ograniczenia są spełnione, gdy zadanie jest dodawane do kolejki, system może zdecydować o natychmiastowym uruchomieniu zadania. Jeśli nie chcesz, aby zadanie zostało uruchomione natychmiast, możesz określić, że ma się rozpocząć po minimalnym początkowym opóźnieniu.
Oto przykład, jak skonfigurować zadanie, aby uruchamiało się co najmniej 10 minut po dodaniu do kolejki.
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setInitialDelay(10, TimeUnit.MINUTES)
.build();
Chociaż przykład pokazuje, jak ustawić opóźnienie początkowe dla OneTimeWorkRequest, możesz też ustawić opóźnienie początkowe dla PeriodicWorkRequest. W takim przypadku opóźnione zostanie tylko pierwsze uruchomienie zadania okresowego.
Zasady ponawiania i wycofywania
Jeśli chcesz, aby WorkManager ponowił próbę wykonania zadania, możesz zwrócić Result.retry() z workera. Zadanie zostanie wtedy ponownie zaplanowane zgodnie z opóźnieniem wycofywania i zasadami wycofywania.
Opóźnienie wycofywania określa minimalny czas oczekiwania przed ponowną próbą wykonania zadania po pierwszej próbie. Ta wartość nie może być mniejsza niż 10 sekund (lub MIN_BACKOFF_MILLIS).
Zasada wycofywania określa, jak opóźnienie wycofywania powinno zwiększać się w czasie w przypadku kolejnych prób. WorkManager obsługuje 2 zasady wzrastającego czasu do ponowienia:
LINEARiEXPONENTIAL.
Każde żądanie robocze ma zasadę wycofywania i opóźnienie wycofywania. Domyślna zasada to EXPONENTIAL z opóźnieniem wynoszącym 30 sekund, ale możesz to zmienić w konfiguracji żądania zadania.
Oto przykład dostosowywania opóźnienia i zasad wycofywania.
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
W tym przykładzie minimalne opóźnienie ponowienia jest ustawione na minimalną dozwoloną wartość, czyli 10 sekund. Ponieważ zasada to LINEAR, odstęp między próbami będzie się zwiększał o około 10 sekund przy każdej nowej próbie. Na przykład pierwsze uruchomienie, które zakończy się błędem Result.retry(), zostanie ponowione po 10 sekundach, a następnie po 20, 30, 40 itd., jeśli zadanie będzie nadal zwracać błąd Result.retry() po kolejnych próbach. Jeśli zasada wycofywania została ustawiona na EXPONENTIAL, sekwencja czasu ponownej próby byłaby bliższa 20, 40 i 80.
Oznaczanie prac tagami
Każde żądanie zadania ma unikalny identyfikator, który może służyć do identyfikacji tego zadania w celu jego anulowania lub obserwowania postępów.
Jeśli masz grupę logicznie powiązanych zadań, możesz też otagować te elementy robocze. Tagowanie umożliwia pracę z grupą żądań roboczych.
Na przykład WorkManager.cancelAllWorkByTag(String) anuluje wszystkie żądania zadań z określonym tagiem, a WorkManager.getWorkInfosByTag(String) zwraca listę obiektów WorkInfo, których można użyć do określenia bieżącego stanu zadania.
Poniższy kod pokazuje, jak dodać do utworu tag „cleanup”:
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.addTag("cleanup")
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.addTag("cleanup")
.build();
Do jednego żądania pracy można dodać wiele tagów. Wewnętrznie te tagi są przechowywane jako zbiór ciągów znaków. Aby uzyskać zestaw tagów powiązanych z WorkRequest, możesz użyć WorkInfo.getTags().
Z klasy Worker możesz pobrać zestaw tagów za pomocą metody ListenableWorker.getTags().
Przypisz dane wejściowe
Do wykonania zadania mogą być wymagane dane wejściowe. Na przykład zadanie, które obsługuje przesyłanie obrazu, może wymagać identyfikatora URI obrazu do przesłania jako danych wejściowych.
Wartości wejściowe są przechowywane jako pary klucz-wartość w obiekcie Data i można je ustawić w żądaniu zadania. WorkManager dostarczy dane wejściowe Data do zadania, gdy będzie je wykonywać. Klasa Worker może uzyskać dostęp do argumentów wejściowych, wywołując Worker.getInputData(). Poniższy kod pokazuje, jak utworzyć instancję Worker, która wymaga danych wejściowych, i jak wysłać ją w żądaniu zadania.
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 = OneTimeWorkRequestBuilder<UploadWork>()
.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();
Możesz też użyć klasy Data, aby zwrócić wartość.
Następne kroki
Na stronie Stany i obserwacja dowiesz się więcej o stanach zadań i o tym, jak monitorować postępy pracy.