W przewodniku dla początkujących opisaliśmy, jak utworzyć WorkRequest
i dodać go 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 zadań, np. wymagania dotyczące Wi-Fi lub ładowania;
- gwarantowanie minimalnego opóźnienia w wykonywaniu zadań;
- ustawianie strategii ponawiania i wycofywania;
- przekazywanie danych wejściowych do zadań;
- grupowanie powiązanych zadań za pomocą tagów.
Przegląd
Zadania są definiowane w WorkManager za pomocą WorkRequest. Aby zaplanować dowolne zadanie za pomocą WorkManager, 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 WorkManager do zaplanowania i uruchomienia zadania. Obejmuje on ograniczenia, które muszą zostać spełnione, aby zadanie mogło się uruchomić, informacje o planowaniu, takie jak opóźnienia lub powtarzające się interwały, konfigurację ponawiania, a także dane wejściowe, jeśli zadanie ich wymaga.
WorkRequest to abstrakcyjna klasa bazowa. Istnieją 2 implementacje pochodne 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 nie powtarzają się, a PeriodicWorkRequest lepiej nadaje się do planowania zadań, które powtarzają się w określonych odstępach czasu.
Planowanie zadań jednorazowych
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ć narzędzia do tworzenia:
Kotlin
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
// Additional configuration
.build()
Java
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
// Additional configuration
.build();
Planowanie prac priorytetowych
W WorkManager 2.7.0 wprowadziliśmy koncepcję prac priorytetowych. Pozwala to WorkManager wykonywać ważne zadania, jednocześnie zapewniając systemowi lepszą kontrolę nad dostępem do zasobów.
Prace priorytetowe charakteryzują się tymi cechami:
- Ważność: prace priorytetowe są odpowiednie dla zadań, które są ważne dla użytkownika lub zostały przez niego zainicjowane.
- Szybkość: prace priorytetowe najlepiej pasują do krótkich zadań, które rozpoczynają się natychmiast i kończą w ciągu kilku minut.
- Limity: o tym, czy zadanie priorytetowe może się rozpocząć, decyduje limit systemowy, który ogranicza czas wykonywania na pierwszym planie.
- Zarządzanie energią: Ograniczenia zarządzania energią, takie jak Oszczędzanie baterii i uśpienie, rzadziej wpływają na prace priorytetowe.
- Opóźnienie: system natychmiast wykonuje prace priorytetowe, o ile pozwala na to bieżące obciążenie systemu. Oznacza to, że są one wrażliwe na opóźnienia i nie można ich zaplanować na późniejsze wykonanie.
Potencjalnym przypadkiem użycia prac priorytetowych może być aplikacja do czatu, w której użytkownik chce wysłać wiadomość lub załączony obraz. Podobnie aplikacja, która obsługuje płatności lub subskrypcje, może też używać zadań priorytetowych. Wynika to z tego, że te zadania są ważne dla użytkownika, szybko wykonują się w tle, muszą się rozpocząć natychmiast i powinny być wykonywane nawet wtedy, gdy użytkownik zamknie aplikację.
Limity
Aby zadanie priorytetowe mogło się uruchomić, system musi przydzielić mu czas wykonywania. Czas wykonywania nie jest nieograniczony. Każda aplikacja otrzymuje limit czasu wykonywania. Gdy aplikacja wykorzysta swój czas wykonywania i osiągnie przydzielony limit, nie będzie już mogła wykonywać prac priorytetowych, dopóki limit się nie odświeży. Pozwala to Androidowi skuteczniej równoważyć zasoby między aplikacjami.
Ilość czasu wykonywania dostępnego dla aplikacji zależy od zasobnika w trybie gotowości i ważności procesu.
Możesz określić, co się stanie, gdy limit wykonywania nie pozwoli na natychmiastowe uruchomienie zadania priorytetowego. Więcej informacji znajdziesz w tych fragmentach kodu.
Wykonywanie prac priorytetowych
Od WorkManager 2.7 aplikacja może wywoływać setExpedited(), aby zadeklarować, że WorkRequest ma być wykonywane jak najszybciej za pomocą zadania priorytetowego. 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 zadaniem priorytetowym. Jeśli limit na to pozwala, zadanie natychmiast zacznie się wykonywać w tle. Jeśli limit został wykorzystany, parametr OutOfQuotaPolicy wskazuje, że żądanie powinno być wykonywane jako zwykłe zadanie niepriorytetowe.
Zgodność wsteczna i usługi na pierwszym planie
Aby zachować zgodność wsteczną w przypadku zadań priorytetowych, WorkManager może uruchamiać usługę na pierwszym planie w wersjach platformy starszych niż Android 12. Usługi na pierwszym planie mogą wyświetlać użytkownikowi powiadomienie.
Metody getForegroundInfoAsync() i getForegroundInfo() w klasie Worker umożliwiają WorkManager wyświetlanie powiadomienia, gdy wywołasz setExpedited() przed Androidem 12.
Jeśli chcesz, aby zadanie było wykonywane jako zadanie priorytetowe, każda klasa ListenableWorker musi implementować metodę getForegroundInfo.
W przypadku Androida 12 lub nowszego usługi na pierwszym planie są nadal dostępne za pomocą odpowiedniej metody setForeground.
Instancja robocza
Instancje robocze nie wiedzą, czy wykonywane przez nie zadanie jest priorytetowe. Jednak w niektórych wersjach Androida instancje robocze mogą wyświetlać powiadomienie, gdy WorkRequest zostało przyspieszone.
Aby to umożliwić, WorkManager udostępnia metodę getForegroundInfoAsync(), którą musisz zaimplementować, aby WorkManager mógł wyświetlać powiadomienie o uruchomieniu ForegroundService w razie potrzeby.
CoroutineWorker
Jeśli używasz CoroutineWorker, musisz zaimplementować getForegroundInfo(). Następnie przekazujesz ją 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 określić, co się stanie z zadaniem priorytetowym, gdy aplikacja osiągnie limit wykonywania. Aby kontynuować, możesz przekazać setExpedited():
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, co spowoduje, że zadanie będzie wykonywane jako zwykłe żądanie zadania. Pokazuje to wcześniejszy fragment kodu.OutOfQuotaPolicy.DROP_WORK_REQUEST, co spowoduje 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 odroczyć rozpoczęcie nowych prac priorytetowych, np. 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.
- Limit: przekroczono limit zadań priorytetowych. Prace priorytetowe korzystają z systemu limitów, który jest oparty na zasobnikach Czuwania aplikacji i ogranicza maksymalny czas wykonania w ruchomym oknie czasowym. Limity używane w przypadku prac priorytetowych są bardziej restrykcyjne niż te używane w przypadku innych typów zadań w tle.
Planowanie zadań okresowych
Czasami aplikacja może wymagać, aby niektóre zadania były wykonywane okresowo. Możesz na przykład okresowo tworzyć kopie zapasowe danych, pobierać nowe treści w aplikacji lub przesyłać dzienniki na serwer.
Oto jak użyć PeriodicWorkRequest, aby utworzyć obiekt
WorkRequest, który będzie 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 interwałem 1 godziny.
Okres interwału jest definiowany jako minimalny czas między powtórzeniami. Dokładny czas wykonania instancji roboczej zależy od ograniczeń używanych w obiekcie WorkRequest oraz od optymalizacji przeprowadzanych przez system.
Elastyczne interwały wykonywania
Jeśli charakter zadania sprawia, że jest ono wrażliwe na czas wykonywania, możesz skonfigurować
swoje PeriodicWorkRequest, aby było wykonywane w okresie
elastycznym w każdym okresie interwału, jak pokazano na ilustracji 1.

Ilustracja 1. Diagram przedstawia powtarzające się interwały z elastycznym okresem, w którym można wykonać zadanie.
Aby zdefiniować zadanie okresowe z okresem elastycznym, podczas tworzenia PeriodicWorkRequest przekaż flexInterval wraz z repeatInterval. Okres elastyczny zaczyna się od repeatInterval - flexInterval i trwa do końca interwału.
Oto przykład zadania okresowego, które może być wykonywane w ciągu ostatnich 15 minut każdego godzinnego okresu.
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
Możesz stosować ograniczenia do zadań okresowych. Możesz na przykład dodać do żądania zadania ograniczenie, aby zadanie było wykonywane tylko wtedy, gdy urządzenie użytkownika jest ładowane. W takim przypadku nawet jeśli upłynie zdefiniowany interwał powtarzania, PeriodicWorkRequest nie zostanie uruchomione, dopóki ten warunek nie zostanie spełniony. Może to spowodować opóźnienie wykonania zadania lub nawet jego pominięcie, jeśli warunki nie zostaną spełnione w okresie wykonywania.
Ograniczenia dotyczące zadań
Constraints zapewniają, że zadanie zostanie odroczone do momentu spełnienia optymalnych warunków. W WorkManager dostępne są te ograniczenia:
| NetworkType | Ogranicza typ sieci wymagany do wykonania zadania.
Na przykład Wi-Fi (UNMETERED).
|
| BatteryNotLow | Jeśli ta opcja jest ustawiona na true, zadanie nie zostanie wykonane, gdy urządzenie jest w trybie oszczędzania baterii. |
| RequiresCharging | Jeśli ta opcja jest ustawiona na true, zadanie zostanie wykonane tylko wtedy, gdy urządzenie jest ładowane. |
| DeviceIdle | Jeśli ta opcja jest ustawiona na true, przed wykonaniem zadania urządzenie użytkownika musi być w stanie bezczynności. Może to być przydatne do wykonywania operacji wsadowych, które w przeciwnym razie mogłyby negatywnie wpłynąć na wydajność innych aplikacji działających aktywnie na urządzeniu użytkownika. |
| StorageNotLow | Jeśli ta opcja jest ustawiona na true, zadanie nie zostanie wykonane, gdy na urządzeniu użytkownika będzie zbyt mało miejsca na dane. |
Aby utworzyć zestaw ograniczeń i powiązać go z zadaniem, utwórz instancję
Constraints za pomocą Constraints.Builder() i przypisz ją do swojego
WorkRequest.Builder().
Na przykład ten kod tworzy żądanie zadania, 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 wykonane tylko wtedy, gdy wszystkie zostaną spełnione.
Jeśli podczas wykonywania zadania ograniczenie przestanie być spełnione, WorkManager zatrzyma instancję roboczą. Zadanie zostanie ponowione, gdy wszystkie ograniczenia zostaną spełnione.
Opóźnione zadania
Jeśli zadanie nie ma ograniczeń lub wszystkie ograniczenia są spełnione, gdy zadanie jest dodawane do kolejki, system może je natychmiast uruchomić. Jeśli nie chcesz, aby zadanie było wykonywane natychmiast, możesz określić, że ma się ono rozpocząć po minimalnym opóźnieniu początkowym.
Oto przykład, jak ustawić zadanie, aby było wykonywane co najmniej 10 minut po dodaniu go 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();
Ten przykład pokazuje, jak ustawić opóźnienie początkowe dla OneTimeWorkRequest, ale możesz też ustawić opóźnienie początkowe dla PeriodicWorkRequest. W takim przypadku opóźnione zostanie tylko pierwsze wykonanie zadania okresowego.
Zasady ponawiania i czasu do ponowienia
Jeśli chcesz, aby WorkManager ponowił wykonanie zadania, możesz zwrócić
Result.retry() z instancji roboczej. Zadanie zostanie wtedy
ponownie zaplanowane zgodnie z czasem do ponowienia i zasadami wycofywania.
Czas do ponowienia 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).
Zasady ponawiania z opóźnieniem określają, jak opóźnienie ponawiania powinno się zwiększać z czasem w przypadku kolejnych prób ponowienia. WorkManager obsługuje 2 zasady wycofywania:
LINEARiEXPONENTIAL.
Każde żądanie zadania ma zasady ponawiania i opóźnienie ponawiania. Domyślne zasady to EXPONENTIAL z opóźnieniem 30 sekund, ale możesz je zastąpić 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 minimalny czas do ponowienia jest ustawiony na minimalną dozwoloną wartość, czyli 10 sekund. Ponieważ zasady to LINEAR, interwał ponawiania będzie się zwiększać o około 10 sekund z każdą nową próbą. Na przykład pierwsze wykonanie kończące się Result.retry() zostanie ponowione po 10 sekundach, a następnie po 20, 30, 40 itd., jeśli zadanie nadal będzie zwracać Result.retry() po kolejnych próbach. Jeśli zasady czasu do ponowienia byłyby ustawione na EXPONENTIAL, sekwencja czasu ponawiania byłaby bliższa 20, 40 i 80.
Tagowanie zadań
Każde żądanie zadania ma unikalny identyfikator, którego można później użyć do zidentyfikowania zadania w celu jego anulowania lub obserwowania postępu.
Jeśli masz grupę logicznie powiązanych zadań, możesz też otagować te zadania. Tagowanie umożliwia wspólne wykonywanie operacji na grupie żądań zadań.
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.
Ten kod pokazuje, jak dodać do zadania tag „cleanup”:
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.addTag("cleanup")
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.addTag("cleanup")
.build();
Do jednego żądania zadania można dodać kilka tagów. Wewnętrznie te tagi są przechowywane jako zbiór ciągów. Aby uzyskać zbiór tagów powiązanych z
WorkRequest, możesz użyć WorkInfo.getTags().
Z klasy Worker możesz pobrać zbiór tagów za pomocą
ListenableWorker.getTags().
Przypisywanie danych wejściowych
Aby wykonać zadanie, może być konieczne podanie danych wejściowych. Na przykład zadanie, które obsługuje przesyłanie obrazu, może wymagać jako danych wejściowych adresu URI przesyłanego obrazu.
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 je wykona. Klasa Worker może uzyskać dostęp do argumentów wejściowych, wywołując Worker.getInputData(). Ten kod pokazuje, jak utworzyć instancję Worker, która wymaga danych wejściowych, i jak je wysłać 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();
Podobnie możesz użyć klasy Data, aby wygenerować wartość zwracaną.
Następne kroki
Na stronie Stany i obserwacja dowiesz się więcej o stanach zadań i o tym, jak monitorować postęp zadań.