Definiowanie próśb o pracę

Przewodnik dla początkujących zawierał informacje o tym, jak utworzyć prosty WorkRequest i umieścić go w kole.

Z tego przewodnika dowiesz się, jak definiować i dostosowywać obiekty WorkRequest do obsługi typowych przypadków użycia, takich jak:

  • Planowanie zadań jednorazowych i cyklicznych
  • ustawiać ograniczenia dotyczące pracy, takie jak wymaganie Wi-Fi lub ładowania;
  • gwarantować minimalne opóźnienie w wykonywaniu zadań;
  • Ustawianie strategii ponownego próbowania i rezygnacji
  • Przekazywanie danych wejściowych do pracy
  • Grupowanie powiązanych zadań za pomocą tagów

Omówienie

Praca jest definiowana w WorkManager za pomocą WorkRequest. Aby zaplanować pracę z WorkManager, musisz najpierw utworzyć obiekt WorkRequest, a potem umieścić go w kole.

Kotlin

val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)

Java

WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);

Obiekt WorkRequest zawiera wszystkie informacje potrzebne WorkManagerowi do zaplanowania i wykonania pracy. Zawiera 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 i może zawierać dane wejściowe, jeśli są one potrzebne do wykonania zadania.

WorkRequest to abstrakcyjna klasa podstawowa. Istnieją 2 implementacje pochodne tej klasy, których możesz użyć do utworzenia żądania: OneTimeWorkRequestPeriodicWorkRequest. Jak sama nazwa wskazuje, OneTimeWorkRequest jest przydatna do planowania pracy niepowtarzalnej, podczas gdy PeriodicWorkRequest jest bardziej odpowiednia do planowania pracy powtarzalnej w określonych odstępach czasu.

Planowanie zadania jednorazowego

W przypadku prostych prac, 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 wersji WorkManager 2.7.0 wprowadzono pojęcie prac priorytetowych. Dzięki temu WorkManager może wykonywać ważne zadania, zapewniając jednocześnie systemowi lepszą kontrolę nad dostępem do zasobów.

Przyspieszona praca wyróżnia się następującymi cechami:

  • Ważność: przyspieszone działanie jest odpowiednie do zadań, które są ważne dla użytkownika lub są przez niego inicjowane.
  • Szybkość: szybka praca najlepiej nadaje się do 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 w tle, określa, czy zadanie przyspieszone może się rozpocząć.
  • Zarządzanie zasilaniem: ograniczenia związane z zarządzaniem energią, takie jak Oszczędzanie baterii i Uśpienie, rzadziej mają wpływ na przyspieszoną pracę.
  • Czas oczekiwania: system natychmiast wykonuje przyspieszoną pracę, o ile umożliwia to bieżące zadanie systemu. Oznacza to, że uwzględniają one opóźnienia i nie można ich zaplanować do późniejszego wykonania.

Przykładem użycia przyspieszonego przetwarzania może być sytuacja, gdy użytkownik chce wysłać wiadomość lub załączony obraz w aplikacji do czatu. Aplikacja obsługująca też proces płatności lub subskrypcji też może chcieć skorzystać z pracy przyspieszonej. Dzieje się tak, ponieważ te zadania są ważne dla użytkownika, są wykonywane szybko w tle, muszą się rozpocząć natychmiast i powinny być wykonywane nawet wtedy, gdy użytkownik zamknie aplikację.

Limity

Zanim zadanie przyspieszone może zostać uruchomione, system musi przydzielić mu czas na wykonanie. Czas wykonywania nie jest nieograniczony. Każda aplikacja otrzymuje limit czasu wykonywania. Gdy aplikacja wykorzysta czas wykonania i osiągnie przydzielony limit, nie będzie można wykonywać przyspieszonych zadań, dopóki limit nie zostanie odświeżony. Dzięki temu Android może efektywniej przydzielać zasoby aplikacjom.

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 wykonania nie pozwoli na natychmiastowe uruchomienie przyspieszonego zadania. Szczegóły znajdziesz w poniższych fragmentach kodu.

Wykonywanie prac przyspieszonych

Począwszy od WorkManager 2.7 Twoja aplikacja może wywołać funkcję setExpedited(), aby zadeklarować, że WorkRequest powinna działać tak szybko, jak to możliwe, korzystając z przyspieszonego zadania. Poniższy fragment kodu pokazuje, jak używać funkcji setExpedited():

Kotlin

val request = OneTimeWorkRequestBuilder<SyncWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

WorkManager.getInstance(context)
    .enqueue(request)

Java

OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
    .setInputData(inputData)
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build();

W tym przykładzie inicjujemy instancję OneTimeWorkRequest i wywołujemy ją setExpedited(). Wtedy staje się ono prośbą o przyspieszone rozpatrzenie. Jeśli limit nie zostanie przekroczony, proces rozpocznie się natychmiast w tle. Jeśli limit został wykorzystany, parametr OutOfQuotaPolicy wskazuje, że żądanie powinno zostać wykonane jako zwykłe, a nie przyspieszone.

Zgodność wsteczna i usługi na pierwszym planie

Aby zachować zgodność wsteczną na potrzeby przyspieszonego wykonywania zadań, WorkManager może uruchamiać usługi 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 instancji roboczej umożliwiają usłudze WorkManager wyświetlanie powiadomień przy wywołaniu funkcji setExpedited() przed Androidem 12.

Jeśli chcesz, aby zadanie było wykonywane jako zadanie przyspieszone, każda usługa 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.

Worker

Pracownicy nie wiedzą, czy wykonują zadania w ramach usługi przyspieszonej obsługi. Jednak w niektórych wersjach Androida pracownicy mogą wyświetlać powiadomienie, gdy WorkRequest zostanie przyspieszone.

Aby włączyć tę funkcję, WorkManager udostępnia metodę getForegroundInfoAsync(), którą musisz zaimplementować, aby w razie potrzeby WorkManager mógł wyświetlać powiadomienie o uruchamianiu ForegroundService za Ciebie.

CoroutineWorker

Jeśli używasz metody CoroutineWorker, musisz zaimplementować getForegroundInfo(). Następnie przekaż je dostawcy setForeground() w ciągu doWork(). Spowoduje to utworzenie powiadomienia w wersjach Androida starszych niż 12.

Przyjrzyjmy 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 przyspieszoną pracą, gdy aplikacja osiągnie limit wykonania. Aby kontynuować, możesz przekazać setExpedited():

  • OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, co powoduje, że zadanie jest uruchamiane jako zwykłe żądanie robocze. Przykładowy fragment kodu
  • OutOfQuotaPolicy.DROP_WORK_REQUEST, co powoduje anulowanie żądania, jeśli limit jest niewystarczający.

Przykładowa aplikacja

Pełny przykład wykorzystania przyspieszonej pracy w WorkManager 2.7.0 znajdziesz w pliku WorkManagerSample na GitHubie.

Odroczone prace priorytetowe

System stara się wykonać dane przyspieszone zadanie tak szybko, jak to możliwe po jego wywołaniu. Jednak, podobnie jak w przypadku innych typów zadań, system może odroczyć rozpoczęcie nowych zadań przyspieszonych, jak w następujących przypadkach:

  • Obciążenie: obciążenie systemu jest zbyt duże, co może wystąpić, gdy uruchomionych jest zbyt wiele zadań lub gdy w systemie jest za mało pamięci.
  • Limit: limit przyspieszonego zadania został przekroczony. Przyspieszone przetwarzanie korzysta z systemu limitów, który jest oparty na zasobach aplikacji w stanie gotowości, i ogranicza maksymalny czas wykonywania w ramach ruchomego okna czasowego. Limity używane na potrzeby pracy przyspieszonej są bardziej restrykcyjne niż w przypadku innych typów zadań w tle.

Planowanie zadań okresowych

Czasami aplikacja może wymagać okresowego wykonywania określonych czynności. Możesz na przykład okresowo tworzyć kopie zapasowe danych, pobierać nowe treści w aplikacji lub przesyłać dzienniki na serwer.

Aby utworzyć obiekt WorkRequest, który jest okresowo wykonywany, za pomocą polecenia PeriodicWorkRequest:

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.

Okres przerwy to minimalny czas między powtórzeniami. Dokładny czas wykonania instancji roboczej zależy od ograniczeń używanych w obiekcie WorkRequest i od optymalizacji wykonywanych przez system.

Elastyczne interwały biegów

Jeśli ze względu na charakter Twojej pracy czas wykonania jest istotny, możesz skonfigurować PeriodicWorkRequest tak, aby działał w okresie elastyczności w ramach każdego przedziału czasowego, jak pokazano na rysunku 1.

Możesz ustawić interwał elastyczny dla zadania okresowego. Określasz interwał powtarzania oraz interwał elastyczny, który określa pewien czas na końcu tego interwału powtarzania. WorkManager próbuje uruchomić zadanie w okresie elastycznym w ramach każdego cyklu.

Rysunek 1. Diagram pokazuje przedziały powtórzeń z elastycznym okresem, w którym można uruchomić zadanie.

Aby zdefiniować pracę okresową z okresem elastycznym, podczas tworzenia zadania PeriodicWorkRequest należy przekazać parametr flexInterval wraz z parametrem repeatInterval. Okres elastyczny rozpoczyna się w repeatInterval - flexInterval i kończy się wraz z zakończeniem przedziału.

Poniżej przedstawiamy przykład zadania okresowego, które może być wykonywane w ostatnich 15 minutach 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 nie może być mniejszy niż PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, a interwał elastyczny musi być większy lub równy PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS.

Wpływ ograniczeń na pracę okresową

Możesz stosować ograniczenia do pracy okresowej. Możesz na przykład dodać do żądania pracy 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, działanie PeriodicWorkRequest nie zostanie wykonane, dopóki nie spełni się to kryterium. Może to spowodować opóźnienie lub nawet pominięcie określonego uruchomienia, jeśli warunki nie zostaną spełnione w okresie wykonywania.

Ograniczenia dotyczące pracy

Ograniczenia powodują, że zadania są odkładane do momentu spełnienia optymalnych warunków. WorkManager obsługuje te ograniczenia.

NetworkType Ogranicza typ sieci wymagany do wykonania zadania. Na przykład Wi-Fi (UNMETERED).
Niski poziom baterii Jeśli zasada ma wartość Prawda, praca nie będzie wykonywana, gdy urządzenie będzie działać w trybie niskiego poziomu baterii.
Wymaga ładowania Gdy ta opcja ma wartość Prawda, zadanie będzie wykonywane tylko wtedy, gdy urządzenie jest ładowane.
Brak urządzenia Jeśli zasada ma wartość Prawda, urządzenie użytkownika jest nieaktywne, aby można było uruchomić pracę. Może to być przydatne podczas wykonywania operacji zbiorczych, które mogłyby negatywnie wpłynąć na wydajność innych aplikacji działających na urządzeniu użytkownika.
Za mało miejsca na dane Jeśli zasada ma wartość Prawda, pliki nie są uruchamiane, jeśli na urządzeniu użytkownika jest za mało miejsca.

Aby utworzyć zestaw ograniczeń i powiązać go z zadaniem, utwórz instancję Constraints za pomocą elementu Contraints.Builder() i przypisz ją do elementu WorkRequest.Builder().

Na przykład poniższy kod tworzy żądanie pracy, które działa tylko wtedy, gdy urządzenie użytkownika jest ładowane i podłączone do sieci 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 wiele ograniczeń, Twoja praca będzie wykonywana tylko wtedy, gdy zostaną spełnione wszystkie ograniczenia.

Jeśli podczas wykonywania pracy wystąpi ograniczenie, WorkManager zatrzyma zadanie. Następnie zostanie ono powtórzone, gdy zostaną spełnione wszystkie ograniczenia.

Opóźniona praca

Jeśli Twoje zadanie nie ma żadnych ograniczeń lub wszystkie ograniczenia są spełnione, gdy zadanie jest umieszczane w kolejce, system może zdecydować się na jego uruchomienie natychmiast. Jeśli nie chcesz, aby zadanie było wykonywane natychmiast, możesz określić, aby rozpoczęło się po minimalnym początkowym opóźnieniu.

Oto przykład ustawienia pracy tak, aby była wykonywana co najmniej 10 minut po umieszczeniu w kolejce.

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

Java

WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

Przykład pokazuje, jak ustawić początkowe opóźnienie dla OneTimeWorkRequest, ale możesz też ustawić początkowe opóźnienie dla PeriodicWorkRequest. W takim przypadku opóźni się tylko pierwsze uruchomienie okresowego zadania.

Zasada ponawiania i wycofywania

Jeśli chcesz, aby usługa WorkManager ponownie wykonała zadanie, możesz zwrócić Result.retry() od instancji roboczej. Następnie Twoja praca zostanie ponownie zaplanowana zgodnie z opóźnieniem i zasadami odroczenia.

  • Opóźnienie wycofania określa minimalny czas oczekiwania przed podjęciem kolejnej próby po pierwszej próbie. Ta wartość nie może być mniejsza niż 10 sekund (lub MIN_BACKOFF_MILLIS).

  • Zasady wycofywania określają, jak ma się zwiększać opóźnienie wycofywania w kolejnych próbach. WorkManager obsługuje 2 zasady wycofywania: LINEAREXPONENTIAL.

Każde żądanie pracy ma zasadę ponowienia i opóźnienia. Domyślna zasada to EXPONENTIAL z opóźnieniem 30 sekund, ale możesz to zmienić w konfiguracji żądania służbowego.

Oto przykład dostosowywania zasady opóźnienia ponowienia i zasady.

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

Java

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setBackoffCriteria(
                       BackoffPolicy.LINEAR,
                       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                       TimeUnit.MILLISECONDS)
               .build();

W tym przykładzie minimalny czas odroczenia jest ustawiony na minimalną dozwoloną wartość, czyli 10 sekund. Zgodnie z tą zasadą LINEAR odstęp między próbami będzie się zwiększał o około 10 sekund przy każdej kolejnej próbie. Na przykład pierwsze uruchomienie kończące się wynikiem Result.retry() zostanie powtórzone po 10 sekundach, a następnie po 20, 30, 40 i tak dalej, jeśli po kolejnych próbach nadal zwraca wartość Result.retry(). Gdyby zasada wycofywania miała wartość EXPONENTIAL, sekwencja czasu ponownego próby byłaby bliższa 20, 40, 80 i tak dalej.

Otaguj pracę

Każde żądanie zadania ma unikalny identyfikator, który umożliwia później identyfikację tych zadań w celu anulowania zadania lub obserwowania jego postępów.

Jeśli masz grupę elementów pracy powiązanych logicznie, możesz też oznaczyć te elementy. Tagowanie umożliwia grupowanie zapytań o pracę.

Na przykład WorkManager.cancelAllWorkByTag(String) anuluje wszystkie żądania robocze 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 zadania tag „oczyszczanie”:

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .addTag("cleanup")
   .build()

Java

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
       .addTag("cleanup")
       .build();

Do jednego żądania dotyczącego pracy można dodać wiele tagów. Wewnętrznie tagi te są przechowywane jako zbiór ciągów znaków. Aby uzyskać zestaw tagów powiązanych z WorkRequest, możesz użyć metody WorkInfo.getTags().

Z klasy Worker możesz pobrać zestaw tagów za pomocą polecenia ListenableWorker.getTags().

Przypisywanie danych wejściowych

Twoje zadania mogą wymagać danych wejściowych. Na przykład zadanie, które obsługuje przesyłanie obrazu, może wymagać podania identyfikatora URI obrazu jako danych wejściowych.

Wartości wejściowe są przechowywane w postaci par klucz-wartość w obiekcie Data i można je ustawić w żądaniu roboczym. WorkManager przekaże dane wejściowe Data Twojemu zadaniu, gdy je wykona. Klasa Worker może uzyskać dostęp do argumentów wejściowych, wywołując funkcję Worker.getInputData(). Kod poniżej pokazuje, jak utworzyć instancję Worker, która wymaga danych wejściowych, oraz 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();

I podobnie, klasy Data można użyć do wygenerowania zwracanej wartości. Dane wejściowe i wyjściowe są omawiane bardziej szczegółowo w sekcji parametry wejściowe i wartości zwracane.

Dalsze kroki

Na stronie Stany i obserwacja znajdziesz więcej informacji o stanach pracy i sposobach monitorowania ich postępów.