Gdy zdefiniujesz
Worker i
WorkRequest,
ostatnim krokiem jest dodanie zadania do kolejki. Najprostszym sposobem na dodanie zadania do kolejki jest wywołanie metody enqueue() WorkManagera i przekazanie WorkRequest, które chcesz uruchomić.
Kotlin
val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)
Java
WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);
Dodając zadanie do kolejki, zachowaj ostrożność, aby uniknąć duplikowania. Na przykład aplikacja może próbować przesyłać swoje logi do usługi backendu co 24 godziny. Jeśli nie będziesz uważać, możesz dodać to samo zadanie do kolejki wiele razy, mimo że powinno ono zostać wykonane tylko raz. Aby to osiągnąć, możesz zaplanować zadanie jako unikalne zadanie.
Unikalne zadanie
Unikalne zadanie to zaawansowana koncepcja, która gwarantuje, że w danym momencie będzie istniała tylko 1 instancja zadania o określonej nazwie. W przeciwieństwie do identyfikatorów unikalne nazwy są czytelne dla człowieka i określane przez dewelopera, a nie generowane automatycznie przez WorkManagera. W przeciwieństwie do tagów unikalne nazwy są powiązane tylko z 1 instancją zadania.
Unikalne zadanie można zastosować zarówno do zadań jednorazowych, jak i okresowych. Aby utworzyć unikalną sekwencję zadań, wywołaj jedną z tych metod w zależności od tego, czy planujesz zadanie powtarzające się, czy jednorazowe.
WorkManager.enqueueUniqueWork()w przypadku zadania jednorazowegoWorkManager.enqueueUniquePeriodicWork()w przypadku zadania okresowego
Obie te metody przyjmują 3 argumenty:
- uniqueWorkName –
Stringużywany do jednoznacznego identyfikowania żądania zadania. - existingWorkPolicy –
enum, który informuje WorkManagera, co ma zrobić , jeśli istnieje już niedokończony łańcuch zadań o tej unikalnej nazwie. Więcej informacji znajdziesz w zasadach dotyczących rozwiązywania konfliktów. - work –
WorkRequest, które ma zostać zaplanowane.
Dzięki unikalnemu zadaniu możemy rozwiązać problem z duplikowaniem planowania, o którym wspomnieliśmy wcześniej.
Kotlin
val sendLogsWorkRequest =
PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest
)
Java
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);
Jeśli kod zostanie uruchomiony, gdy zadanie sendLogs jest już w kolejce, istniejące zadanie zostanie zachowane, a nowe nie zostanie dodane.
Unikalne sekwencje zadań mogą być też przydatne, jeśli musisz stopniowo tworzyć długi łańcuch zadań. Na przykład aplikacja do edycji zdjęć może umożliwiać użytkownikom cofanie długiego łańcucha działań. Każda z tych operacji cofania może potrwać jakiś czas, ale muszą one zostać wykonane w odpowiedniej kolejności. W takim przypadku aplikacja może utworzyć łańcuch „cofnij” i w razie potrzeby dołączyć do niego każdą operację cofania. Więcej informacji znajdziesz w artykule Łączenie zadań w łańcuchy.
Zasady rozwiązywania konfliktów
Podczas planowania unikalnego zadania musisz poinformować WorkManagera, jakie działanie ma podjąć w przypadku konfliktu. Aby to zrobić, podczas dodawania zadania do kolejki przekaż enum.
W przypadku zadania jednorazowego podaj
ExistingWorkPolicy, które
obsługuje 4 opcje rozwiązywania konfliktu.
REPLACEzastępuje istniejące zadanie nowym. Ta opcja anuluje istniejące zadanie.KEEPzachowuje istniejące zadanie i ignoruje nowe.APPENDdodaje nowe zadanie na końcu istniejącego. Ta zasada spowoduje, że nowe zadanie zostanie połączone z istniejącym i będzie wykonywane po jego zakończeniu.
Istniejące zadanie staje się wymaganiem wstępnym dla nowego zadania. Jeśli istniejące zadanie
zostanie CANCELLED lub FAILED, nowe zadanie również zostanie CANCELLED lub FAILED.
Jeśli chcesz, aby nowe zadanie było wykonywane niezależnie od stanu istniejącego zadania, użyj zamiast tego APPEND_OR_REPLACE.
APPEND_OR_REPLACEdziała podobnie jakAPPEND, ale nie zależy od wymagania wstępnego stanu pracy. Jeśli istniejące zadanie zostanieCANCELLEDlubFAILED, nowe zadanie nadal będzie wykonywane.
W przypadku zadania okresowego podaj
ExistingPeriodicWorkPolicy,
które obsługuje 2 opcje: REPLACE i KEEP. Te opcje działają tak samo jak ich odpowiedniki w ExistingWorkPolicy.
Obserwowanie zadań
W dowolnym momencie po dodaniu zadania do kolejki możesz sprawdzić jego stan, wysyłając zapytanie do WorkManagera według jego name, id lub tag powiązanego z nim.
Kotlin
// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
Java
// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
Zapytanie zwraca
ListenableFuture
obiektu WorkInfo, który zawiera
id zadania, jego tagi, jego
bieżący State i wszelkie wyjściowe zbiory danych używające
Result.success(outputData).
Warianty LiveData i Flow
każdej z metod umożliwiają obserwowanie zmian w
WorkInfo przez zarejestrowanie odbiorcy. Jeśli na przykład chcesz wyświetlić użytkownikowi komunikat, gdy jakieś zadanie zostanie wykonane, możesz skonfigurować je w ten sposób:
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect{ workInfo ->
if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show()
}
}
Java
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();
}
});
Złożone zapytania dotyczące zadań
WorkManager w wersji 2.4.0 i nowszych obsługuje złożone zapytania dotyczące zadań w kolejce za pomocą
WorkQuery obiektów. WorkQuery umożliwia wysyłanie zapytań o zadania na podstawie kombinacji ich tagów, stanu i unikalnej nazwy zadania.
Poniższy przykład pokazuje, jak znaleźć wszystkie zadania z tagiem „syncTag”,
które są w stanie FAILED lub CANCELLED i mają unikalną nazwę zadania
„preProcess” lub „sync”.
Kotlin
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)
Java
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);
Każdy komponent (tag, stan lub nazwa) w WorkQuery jest łączony z innymi za pomocą operatora AND. Każda wartość w komponencie jest OR-ed. Na przykład: (name1 OR name2
OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).
WorkQuery działa też z odpowiednikiem LiveData,
getWorkInfosLiveData(),
oraz odpowiednikiem Flow, getWorkInfosFlow().
Anulowanie i zatrzymywanie zadań
Jeśli nie chcesz już, aby wcześniej dodane do kolejki zadanie zostało wykonane, możesz poprosić o jego anulowanie. Zadanie można anulować według jego name, id lub tag powiązanego z nim.
Kotlin
// by id
workManager.cancelWorkById(syncWorker.id)
// by name
workManager.cancelUniqueWork("sync")
// by tag
workManager.cancelAllWorkByTag("syncTag")
Java
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
WorkManager sprawdza
State zadania. Jeśli zadanie zostało
już zakończone,
nic się nie stanie. W przeciwnym razie stan zadania zostanie zmieniony na
CANCELLED i zadanie
nie zostanie wykonane w przyszłości. Wszystkie zadania
WorkRequest, które są zależne
od tego zadania, również zostaną CANCELLED.
RUNNING zadanie
otrzymuje wywołanie
ListenableWorker.onStopped().
Zastąp tę metodę, aby obsłużyć potencjalne czyszczenie. Więcej informacji znajdziesz w artykule Zatrzymywanie działającego workera.
Zatrzymywanie działającego workera
Istnieje kilka powodów, dla których WorkManager może zatrzymać działającego Worker:
- Wyraźnie poprosiłeś(-aś) o jego anulowanie (np. przez wywołanie
WorkManager.cancelWorkById(UUID)). - W przypadku unikalnego zadania,
wyraźnie dodałeś(-aś) do kolejki nowe
WorkRequestzExistingWorkPolicyustawionym naREPLACE. StareWorkRequestjest natychmiast uznawane za anulowane. - Ograniczenia zadania nie są już spełnione.
- System polecił aplikacji zatrzymać zadanie z jakiegoś powodu. Może się to zdarzyć, jeśli przekroczysz limit czasu wykonania wynoszący 10 minut. Zadanie jest zaplanowane do ponowienia w późniejszym czasie.
W tych warunkach worker zostaje zatrzymany.
Powinieneś(-aś) przerwać wszystkie trwające zadania i zwolnić wszystkie zasoby, które są używane przez workera. W tym momencie należy na przykład zamknąć otwarte uchwyty do baz danych i plików. Masz do dyspozycji 2 mechanizmy, które pozwalają sprawdzić, kiedy worker się zatrzymuje.
Wywołanie zwrotne onStopped()
WorkManager invokes
ListenableWorker.onStopped()
natychmiast po zatrzymaniu workera. Zastąp tę metodę, aby zamknąć wszystkie zasoby, które mogą być używane.
Właściwość isStopped()
Możesz wywołać metodę
ListenableWorker.isStopped(), aby sprawdzić, czy worker został już
zatrzymany. Jeśli w workerze wykonujesz długotrwałe lub powtarzające się operacje, często sprawdzaj tę właściwość i używaj jej jako sygnału do jak najszybszego zatrzymania zadania.
Uwaga: WorkManager ignoruje
Result ustawiony przez workera
który otrzymał sygnał onStop, ponieważ worker jest już uznawany za
zatrzymanego.