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 pracy jednorazowej i cyklicznej,
- ustawianie ograniczeń pracy, np. wymaganie Wi-Fi lub ładowania,
- gwarantowanie minimalnego opóźnienia w wykonywaniu pracy,
- ustawianie strategii ponawiania i wycofywania,
- przekazywanie danych wejściowych do pracy,
- grupowanie powiązanych zadań za pomocą tagów.
Przegląd
Praca jest definiowana w WorkManager za pomocą WorkRequest. Aby zaplanować dowolną pracę 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 pracy. Obejmuje on ograniczenia, które muszą zostać spełnione, aby praca mogła zostać wykonana, informacje o planowaniu, takie jak opóźnienia lub powtarzające się interwały, konfigurację ponawiania oraz dane wejściowe, jeśli praca 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 pracy, która nie powtarza się, a PeriodicWorkRequest lepiej nadaje się do planowania pracy, która powtarza się w określonych odstępach czasu.
Planowanie pracy jednorazowej
W przypadku podstawowej pracy, która nie wymaga 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żonej pracy 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 pracy priorytetowej
W WorkManager 2.7.0 wprowadziliśmy koncepcję pracy priorytetowej. Umożliwia to WorkManager wykonywanie ważnych zadań, jednocześnie zapewniając systemowi lepszą kontrolę nad dostępem do zasobów.
Praca priorytetowa charakteryzuje się tymi cechami:
- Ważność: praca priorytetowa jest odpowiednia w przypadku zadań, które są ważne dla użytkownika lub zostały zainicjowane przez użytkownika.
- Szybkość: praca priorytetowa najlepiej sprawdza się w przypadku 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 na poziomie systemu, który ogranicza czas wykonywania na pierwszym planie.
- Zarządzanie energią: ograniczenia dotyczące zarządzania energią, takie jak oszczędzanie baterii i Doze, rzadziej wpływają na pracę priorytetową.
- Opóźnienie: system natychmiast wykonuje pracę priorytetową, o ile pozwala na to bieżące obciążenie systemu. Oznacza to, że praca priorytetowa jest wrażliwa na opóźnienia i nie można jej zaplanować na później.
Potencjalnym przypadkiem użycia pracy priorytetowej 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ść lub subskrypcję, może też używać pracy priorytetowej. 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ć pracy priorytetowej, 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 pracy priorytetowej
Od WorkManager 2.7 aplikacja może wywoływać setExpedited(), aby zadeklarować, że WorkRequest ma być wykonywany 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 pracą priorytetową. Jeśli limit na to pozwala, praca rozpocznie się natychmiast w tle. Jeśli limit został wykorzystany, parametr OutOfQuotaPolicy wskazuje, że żądanie powinno być wykonywane jako zwykła praca niepriorytetowa.
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ć powiadomienia dla użytkownika.
Metody getForegroundInfoAsync() i getForegroundInfo() w klasie Worker umożliwiają WorkManager wyświetlanie powiadomienia, gdy wywołasz setExpedited() w wersji Androida starszej niż 12.
Jeśli chcesz, aby zadanie było wykonywane jako zadanie priorytetowe, każda klasa ListenableWorker musi implementować metodę getForegroundInfo.
W przypadku aplikacji kierowanych na Androida 12 lub nowszą wersję usługi na pierwszym planie są nadal dostępne za pomocą odpowiedniej metody setForeground.
Instancja robocza
Instancje robocze nie wiedzą, czy wykonywana przez nie praca jest priorytetowa. Jednak w niektórych wersjach Androida instancje robocze mogą wyświetlać powiadomienie, gdy WorkRequest został przyspieszony.
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 pracą priorytetową, gdy aplikacja osiągnie limit wykonywania. Aby kontynuować, możesz przekazać setExpedited():
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, który powoduje, że zadanie jest wykonywane jako zwykłe żądanie pracy. Pokazuje to wcześniejszy fragment kodu.OutOfQuotaPolicy.DROP_WORK_REQUEST, który powoduje anulowanie żądania, jeśli nie ma wystarczającego limitu.
Odroczona praca priorytetowa
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 nowej pracy priorytetowej, np. w tych przypadkach:
- Obciążenie: obciążenie systemu jest zbyt duże, co może się zdarzyć, gdy działa już zbyt wiele zadań lub gdy system nie ma wystarczającej ilości pamięci.
- Limit: przekroczono limit zadań priorytetowych. Praca priorytetowa korzysta z systemu limitów, który jest oparty na zasobnikach czuwania aplikacji i ogranicza maksymalny czas wykonywania w ruchomym oknie czasowym. Limity używane w przypadku pracy priorytetowej są bardziej restrykcyjne niż te używane w przypadku innych typów zadań w tle.
Planowanie pracy okresowej
Czasami aplikacja może wymagać okresowego wykonywania określonej pracy. 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 praca jest zaplanowana 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 Twojej pracy sprawia, że jest ona wrażliwa na czas wykonywania, możesz skonfigurować
PeriodicWorkRequest, aby była wykonywana w elastycznym
okresie 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ć pracę.
Aby zdefiniować pracę okresową z elastycznym okresem, podczas tworzenia PeriodicWorkRequest przekaż flexInterval wraz z repeatInterval. Elastyczny okres rozpoczyna się od repeatInterval - flexInterval i trwa do końca interwału.
Oto przykład pracy okresowej, która może być wykonywana 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 elastyczny
interwał musi być większy lub równy
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS`.
Wpływ ograniczeń na pracę okresową
Do pracy okresowej możesz stos0ować ograniczenia. Możesz na przykład dodać do żądania pracy ograniczenie, aby praca była wykonywana tylko wtedy, gdy urządzenie użytkownika jest ładowane. W takim przypadku nawet jeśli upłynie zdefiniowany interwał powtarzania, PeriodicWorkRequest nie zostanie uruchomiony, dopóki nie zostanie spełniony ten warunek. Może to spowodować opóźnienie lub nawet pominięcie danego wykonania pracy, jeśli warunki nie zostaną spełnione w interwale wykonywania.
Ograniczenia pracy
Constraints zapewniają, że praca jest odraczana do momentu spełnienia optymalnych warunków. W WorkManager dostępne są te ograniczenia:
| NetworkType | Ogranicza typ sieci wymagany do wykonania pracy.
Na przykład Wi-Fi (UNMETERED).
|
| BatteryNotLow | Jeśli ta opcja jest ustawiona na true, praca nie będzie wykonywana, gdy urządzenie jest w trybie oszczędzania baterii. |
| RequiresCharging | Jeśli ta opcja jest ustawiona na true, praca będzie wykonywana tylko wtedy, gdy urządzenie jest ładowane. |
| DeviceIdle | Jeśli ta opcja jest ustawiona na true, przed wykonaniem pracy 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, praca nie będzie wykonywana, gdy na urządzeniu użytkownika będzie zbyt mało miejsca na dane. |
Aby utworzyć zestaw ograniczeń i powiązać go z pracą, utwórz instancję
Constraints za pomocą Constraints.Builder() i przypisz ją do swojego
WorkRequest.Builder().
Na przykład ten kod tworzy żądanie pracy, 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ń, praca będzie wykonywana tylko wtedy, gdy wszystkie zostaną spełnione.
Jeśli podczas wykonywania pracy ograniczenie przestanie być spełnione, WorkManager zatrzyma instancję roboczą. Praca zostanie ponowiona, gdy wszystkie ograniczenia zostaną spełnione.
Praca opóźniona
Jeśli praca nie ma ograniczeń lub wszystkie ograniczenia są spełnione, gdy praca jest dodawana do kolejki, system może zdecydować się na natychmiastowe wykonanie pracy. Jeśli nie chcesz, aby praca była wykonywana natychmiast, możesz określić, że ma się rozpocząć po minimalnym opóźnieniu początkowym.
Oto przykład, jak ustawić pracę tak, aby była wykonywana 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();
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 pracy okresowej.
Zasady ponawiania i czasu do ponowienia
Jeśli chcesz, aby WorkManager ponowił wykonanie pracy, możesz zwrócić
Result.retry() z instancji roboczej. Praca zostanie wtedy
ponownie zaplanowana zgodnie z opóźnieniem czasu do ponowienia i zasadami czasu do ponowienia.
Czas do ponowienia określa minimalny czas oczekiwania przed ponowną próbą wykonania pracy 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 pracy 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 pracy.
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 praca będzie nadal 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 pracy
Każde żądanie pracy ma unikalny identyfikator, którego można użyć do późniejszego zidentyfikowania pracy w celu jej anulowania lub obserwowania jej 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ń pracy.
Na przykład WorkManager.cancelAllWorkByTag(String) anuluje
wszystkie żądania pracy 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 pracy.
Ten kod pokazuje, jak dodać do pracy 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ć kilka tagów. Wewnętrznie tagi te są przechowywane jako zbiór ciągów. Aby uzyskać zbiór tagów powiązanych z
WorkRequest, możesz użyć WorkInfo.getTags().
W klasie Worker możesz pobrać zbiór tagów za pomocą
ListenableWorker.getTags().
Przypisywanie danych wejściowych
Aby wykonać pracę, może być konieczne podanie danych wejściowych. Na przykład praca, która 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 pracy. WorkManager dostarczy dane wejściowe Data do pracy, gdy ją 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 pracy.
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 zwrócić wartość.
Następne kroki
Na stronie Stany i obserwacja dowiesz się więcej o stanach pracy i o tym, jak monitorować postęp pracy.