Zarządzanie pracą

Po zdefiniowaniu Worker i WorkRequest musisz umieścić swoją pracę w kolejce. Najprostszym sposobem umieszczania pracy w kolejce jest wywołanie metody enqueue() w usłudze WorkManager i przekazanie zdarzenia 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);

Zachowaj ostrożność podczas dodawania pracy do kolejki, aby uniknąć powielenia. Aplikacja może na przykład próbować przesyłać logi do usługi backendu co 24 godziny. Jeśli nie zachowasz ostrożności, możesz wielokrotnie umieścić to samo zadanie w kolejce, mimo że wystarczy je uruchomić tylko raz. Aby osiągnąć ten cel, możesz zaplanować wykonywanie pracy jako niepowtarzalne zadanie.

Unikalne dzieło

Unikalne treści to zaawansowana koncepcja, która gwarantuje, że w danym momencie masz tylko jedno wystąpienie pracy o konkretnej nazwie. W przeciwieństwie do identyfikatorów unikalne nazwy są zrozumiałe dla człowieka i określane przez programistę, a nie są generowane automatycznie przez WorkManager. W przeciwieństwie do tagów unikalne nazwy są powiązane tylko z jednym wystąpieniem zadania.

Unikalne treści można wykorzystać zarówno do pracy jednorazowej, jak i okresowej. Możesz utworzyć unikalną sekwencję pracy, wywołując jedną z tych metod w zależności od tego, czy planujesz pracę powtarzalną czy jednorazową.

W obu przypadkach mogą być używane 3 argumenty:

  • uniqueWorkNameString używany do jednoznacznej identyfikacji żądania roboczego.
  • existingWorkPolicyenum, który informuje WorkManager o tym, co zrobić, gdy istnieje już niedokończony łańcuch zadań o tej unikalnej nazwie. Więcej informacji znajdziesz w zasadach dotyczących rozwiązywania konfliktów.
  • workWorkRequest do zaplanowania.

Wykorzystując unikalne nakłady pracy, możemy rozwiązać wspomniany wcześniej problem z ustalaniem harmonogramu.

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);

Teraz, jeśli kod zostanie uruchomiony, gdy zadanie sendLogs jest już w kolejce, istniejące zadanie zostanie zachowane i nie zostanie dodane żadne nowe zadanie.

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 pozwalać użytkownikom na cofanie wielu czynności. Cofanie tych operacji może trochę potrwać, ale trzeba je wykonywać we właściwej kolejności. W tym przypadku aplikacja może utworzyć łańcuch cofania i w razie potrzeby dołączać do łańcucha każdą operację cofania. Więcej informacji znajdziesz w sekcji Praca łańcuchów.

Zasady rozwiązywania konfliktów

Planując unikalne zadania, musisz poinformować zespół WorkManager, co ma zrobić, gdy wystąpi konflikt. W tym celu należy przekazać zadanie w kolejce.

W przypadku zadań jednorazowych podajesz właściwość ExistingWorkPolicy, która obsługuje 4 opcje rozwiązywania konfliktu.

  • REPLACE praca z nową pracą. Ta opcja anuluje istniejące zadanie.
  • KEEP istniejący utwór i zignoruj nowy.
  • APPEND nową pracę do końca istniejącego. Ta zasada powoduje, że nowa praca jest powiązana z istniejącym zadaniem po jego zakończeniu.

Istniejące utwory staje się warunkem wstępnym nowego. Jeśli istniejące zadanie to CANCELLED lub FAILED, nowy utwór też jest CANCELLED lub FAILED. Jeśli chcesz, aby nowa praca była wykonywana niezależnie od stanu istniejącego, użyj zamiast tego polecenia APPEND_OR_REPLACE.

  • APPEND_OR_REPLACE działa podobnie do APPEND z tą różnicą, że nie jest zależne od stanu zadania wstępnego. Jeśli dotychczasowe zadanie to CANCELLED lub FAILED, nowa wersja wciąż działa.

W przypadku okresu próbnego podajesz wartość ExistingPeriodicWorkPolicy, która obsługuje 2 opcje: REPLACE i KEEP. Te opcje działają tak samo jak ich odpowiedniki IstniejąceWorkPolicy.

Obserwowanie pracy

W każdej chwili po dodaniu zadań do kolejki możesz sprawdzić ich stan, wysyłając zapytanie do WorkManagera za pomocą name, id lub powiązanego z nim tag.

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 utworu, jego tagi, aktualny State oraz wszelkie dane wyjściowe zbioru za pomocą funkcji Result.success(outputData).

Wariant LiveData każdej z metod umożliwia obserwację zmian w elemencie WorkInfo przez zarejestrowanie odbiornika. Jeśli na przykład chcesz, by po pomyślnym zakończeniu jakiejś pracy wyświetlała się wiadomość, możesz ją skonfigurować w ten sposób:

Kotlin


workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { 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 pracy

WorkManager w wersji 2.4.0 i nowszych obsługuje złożone zapytania dotyczące zadań umieszczonych w kolejce przy użyciu obiektów WorkQuery. WorkQuery obsługuje zapytania służbowe z uwzględnieniem tagów, stanu i unikalnej nazwy pracy.

Poniższy przykład pokazuje, jak znaleźć wszystkie operacje z tagiem “syncTag”, które ma stan FAILED lub CANCELLED i ma niepowtarzalną nazwę „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 elemencie WorkQuery jest łączony z innymi komponentami (AND). Każda wartość w komponencie jest zakodowana za pomocą OR, np. (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).

WorkQuery działa też z odpowiednikiem LiveData getWorkInfosLiveData().

Anulowanie i zatrzymywanie pracy

Jeśli nie potrzebujesz już pracy umieszczonej w kolejce do wykonania, możesz poprosić o jej anulowanie. Dzieło może zostać anulowane przez name, id lub powiązaną z nim tag.

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 praca jest już zakończona, nic się nie dzieje. W przeciwnym razie stan utworu zmienia się na CANCELLED i utwór nie będzie kontynuowany. Wszystkie zadania WorkRequest zależne od tych zadań również będą miały stan CANCELLED.

Obecnie RUNNING zadanie odbiera wywołanie ListenableWorker.onStopped(). Aby móc wykonywać ewentualne operacje czyszczenia, zastąp tę metodę. Więcej informacji znajdziesz w artykule o zatrzymywaniu uruchomionej instancji roboczej.

Zatrzymywanie uruchomionej instancji roboczej

Aplikacja WorkManager może zatrzymać uruchamianie pliku Worker z kilku różnych powodów:

  • Otrzymaliśmy od Ciebie wyraźną prośbę o jego anulowanie (np. przez wywołanie WorkManager.cancelWorkById(UUID)).
  • W przypadku niepowtarzalnego utworu został przez Ciebie jawnie umieszczony w kolejce nowy element WorkRequest z wartością ExistingWorkPolicy o wartości REPLACE. Stary WorkRequest zostanie natychmiast uznany za anulowany.
  • Ograniczenia dotyczące Twojej pracy nie są już spełnione.
  • System z jakiegoś powodu uznał, że aplikacja ma zatrzymać pracę. Może się tak zdarzyć, jeśli przekroczysz termin wykonania wynoszący 10 minut. Ponowne wykonanie zadania zaplanowano na później.

W tych warunkach instancja robocza jest zatrzymana.

Wspólnie przerwijcie wszystkie trwające prace i zwolnij wszystkie zasoby, których ma instancja robocza. Na przykład należy zamknąć uchwyty do bazy danych i plików. Masz do dyspozycji 2 mechanizmy pozwalające dowiedzieć się, kiedy serwer roboczy się zatrzymuje.

onStopped()

WorkManager wywołuje ListenableWorker.onStopped(), gdy tylko instancja robocza zostanie zatrzymana. Zastąp tę metodę, aby zamknąć wszystkie zasoby.

właściwość isStopped()

Możesz wywołać metodę ListenableWorker.isStopped(), aby sprawdzić, czy instancja robocza została już zatrzymana. Jeśli w swojej instancji roboczej 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 pracy.

Uwaga: WorkManager ignoruje Result ustawiony przez instancję roboczą, która otrzymała sygnał onStop, ponieważ instancja robocza jest już uznawana za zatrzymaną.