Definiowanie próśb o pracę

Z przewodnika dla początkujących pokazaliśmy, jak utworzyć prosty WorkRequest i dodać go do kolejki.

Z tego przewodnika dowiesz się, jak definiować i dostosowywać obiekty WorkRequest pod kątem typowych przypadków użycia, na przykład:

  • Planowanie zadań jednorazowych i cyklicznych
  • określać ograniczenia pracy, np. wymagać Wi-Fi lub ładowania.
  • Zagwarantowanie minimalnego opóźnienia w realizacji zadań
  • Ustaw strategie ponawiania i wycofywania
  • Przekazywanie danych wejściowych do pracy
  • Grupuj powiązane działania za pomocą tagów

Przegląd

Praca jest określana w usłudze WorkManager za pomocą WorkRequest. Aby zaplanować pracę z użyciem WorkManager, musisz najpierw utworzyć obiekt WorkRequest, a następnie umieścić go w kolejce.

Kotlin


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

Java


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

Obiekt WorkRequest zawiera wszystkie informacje potrzebne usłudze WorkManager do planowania i uruchamiania zadań. Obejmuje on ograniczenia, które muszą zostać spełnione, aby przebiegała Twoja praca, informacje o harmonogramie (np. opóźnienia lub powtarzające się odstępy) i ponowne próby konfiguracji, a także dane wejściowe, jeśli jest to niezbędne w Twojej pracy.

Sama klasa WorkRequest jest abstrakcyjną klasą bazową. Istnieją 2 wywodzące się z tej klasy implementacje klasy, których możesz użyć do utworzenia żądania: OneTimeWorkRequest i PeriodicWorkRequest. Nazwa OneTimeWorkRequest jest przydatna do planowania pracy, która nie powtarza się, natomiast PeriodicWorkRequest jest bardziej odpowiednia w przypadku powtarzania zadań, które się powtarzają.

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 przyspieszonej

W aplikacji WorkManager 2.7.0 wprowadzono koncepcję przyspieszonej pracy. Dzięki temu WorkManager może wykonywać ważne zadania, a jednocześnie daje systemowi lepszą kontrolę nad dostępem do zasobów.

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

  • Ważność: przyspieszona praca odpowiada zadaniom, które są ważne dla użytkownika lub są inicjowane przez użytkownika.
  • Szybkość: „praca przyspieszona” najlepiej nadaje się do krótkich zadań, które zaczynają się od razu i kończą w ciągu kilku minut.
  • Limity: limit na poziomie systemu, który ogranicza czas wykonywania na pierwszym planie, określa, czy można uruchomić przyspieszone zadanie.
  • 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.

Użycie przyspieszonej pracy może być na przykład dostępne w aplikacji do obsługi czatu, gdy użytkownik chce wysłać wiadomość lub załączony obraz. Aplikacja obsługująca też proces płatności lub subskrypcji też może chcieć skorzystać z pracy przyspieszonej. Zadania te są ważne dla użytkownika, są szybko wykonywane w tle, muszą rozpoczynać się natychmiast i powinny być wykonywane nawet po zamknięciu aplikacji przez użytkownika.

Limity

System musi przydzielić czas wykonania do przyspieszonego zadania, aby można było je uruchomić. 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 równoważyć zasoby pomiędzy aplikacjami.

Czas wykonywania dostępny dla aplikacji zależy od zasobnika w trybie gotowości i ważności procesu.

Możesz określić, co się dzieje, gdy limit wykonania nie pozwala na natychmiastowe uruchomienie przyspieszonego zadania. Szczegóły znajdziesz w poniższych fragmentach kodu.

Wykonywanie prac przyspieszonych

Począwszy od WorkManager 2.7 aplikacja może wywołać setExpedited(), aby zadeklarować, że WorkRequest powinno działać jak najszybciej przy użyciu zadania przyspieszonego. Oto przykład użycia 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(). To żądanie stanie się wówczas procesem przyspieszonym. Jeśli na to pozwala limit, będzie on od razu działać w tle. Jeśli limit został już wykorzystany, parametr OutOfQuotaPolicy wskazuje, że żądanie powinno być uruchamiane w zwykły sposób, bez przyspieszenia.

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 ListenableWorker zostało uruchomione jako zadanie przyspieszone, musi ono implementować metodę getForegroundInfo.

Jeśli kierujesz reklamy na Androida 12 lub nowszego, usługi działające na pierwszym planie pozostają dostępne za pomocą odpowiedniej metody setForeground.

Instancja robocza

Pracownicy nie wiedzą, czy ich praca przebiega szybciej, czy nie. Na niektórych wersjach Androida instancje robocze mogą jednak wyświetlać powiadomienie o przyspieszeniu działania WorkRequest.

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

Komponent roboczy Coroutine

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 ma się stać z pracą przyspieszoną po osiągnięciu limitu wykonywania. Aby kontynuować, możesz mijać setExpedited():

  • OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, co powoduje, że zadanie jest uruchamiane jako zwykłe żądanie robocze. Fragment kodu powyżej to potwierdza.
  • 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.

Przyspieszona praca odroczona

System próbuje wykonać dane przyspieszone zadanie jak najszybciej 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. Praca przyspieszona korzysta z systemu limitów opartego na zasobnikach gotowości aplikacji i ogranicza maksymalny czas wykonywania w okresie obiegowym. Limity używane na potrzeby pracy przyspieszonej są bardziej restrykcyjne niż w przypadku innych typów zadań w tle.

Planowanie zadań okresowych

Aplikacja może czasami wymagać okresowego wykonywania pewnych zadań. Możesz na przykład okresowo tworzyć kopie zapasowe danych, pobierać nowe treści w aplikacji lub przesyłać logi 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 odstępem.

Okres interwału 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 charakter Twojej pracy sprawia, że wykazuje ona wrażliwość na czas wczytywania, możesz skonfigurować PeriodicWorkRequest tak, aby uruchamiał się w okresie elastycznym w każdym przedziale czasu, jak pokazano na rys. 1.

W przypadku zadania okresowego możesz ustawić interwał elastyczny. Definiujesz interwał powtarzania i odstęp elastyczny, który określa określony czas na końcu przedziału powtarzania. WorkManager próbuje uruchomić zadanie w pewnym momencie podczas elastycznego interwału 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 PeriodicWorkRequest musisz przekazać element flexInterval razem z parametrem repeatInterval. Okres elastyczny rozpoczyna się repeatInterval - flexInterval i kończy się na końcu interwału.

Poniżej znajdziesz przykład pracy okresowej, która może być wykonywana w ciągu ostatnich 15 minut 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ą

Do prac okresowych możesz stosować ograniczenia. Można na przykład dodać ograniczenie do żądania dotyczącego pracy, tak aby urządzenie działało tylko wtedy, gdy urządzenie użytkownika się ładuje. W tym przypadku, nawet jeśli upłynie zdefiniowany interwał powtarzania, PeriodicWorkRequest nie będzie działać, dopóki ten warunek nie zostanie spełniony. Może to spowodować opóźnienie konkretnego uruchomienia zadania lub nawet pominięcie go, jeśli warunki nie zostaną spełnione w danym przedziale czasu.

Ograniczenia pracy

Ograniczenia powodują odroczenie zadań do momentu spełnienia optymalnych warunków. W usłudze WorkManager dostępne są poniższe ograniczenia.

Typ sieci Ogranicza typ sieci wymagany do działania. 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 Jeśli ma wartość Prawda, Twoja praca będzie wykonywana tylko podczas ładowania urządzenia.
Brak urządzenia Jeśli zasada ma wartość Prawda, urządzenie użytkownika jest nieaktywne, aby można było uruchomić pracę. Jest to przydatne przy przeprowadzaniu operacji zbiorczych, które w przeciwnym razie mogłyby mieć negatywny wpływ na wydajność innych aplikacji aktywnych aktywnie na urządzeniu użytkownika.
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ć zbiór ograniczeń i powiązać go z pracą, utwórz instancję Constraints za pomocą Contraints.Builder() i przypisz ją do instancji WorkRequest.Builder().

Na przykład ten kod tworzy żądanie robocze, które jest uruchamiane tylko wtedy, gdy urządzenie użytkownika się ładuje i jest 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 wiele ograniczeń, Twoja praca będzie wykonywana tylko wtedy, gdy zostaną spełnione wszystkie ograniczenia.

Jeśli w trakcie pracy ograniczenie przestanie zostać osiągnięte, usługa WorkManager zatrzyma instancję roboczą. Zadanie zostanie ponowione, gdy będą spełnione wszystkie ograniczenia.

Opóźniona praca

Jeśli Twoje zadanie nie ma żadnych ograniczeń lub jeśli podczas dodawania zadania do kolejki są spełnione wszystkie ograniczenia, system może ustawić je natychmiast. Jeśli nie chcesz, aby praca była wykonywana natychmiast, możesz ustawić ją tak, aby rozpoczynała się z minimalnym opóźnieniem początkowym.

Oto przykład, jak ustawić uruchamianie zadania przez co najmniej 10 minut od umieszczenia 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 obiektu OneTimeWorkRequest, ale możesz też ustawić opóźnienie dla zdarzenia 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. Termin pracy zostanie przełożony zgodnie z opóźnieniem wycofywania i zasadami wycofywania.

  • Opóźnienie wycofania określa minimalny czas oczekiwania przed ponowną próbą wykonania czynności po pierwszej próbie. Ta wartość nie może być mniejsza niż 10 sekund (lub MIN_BACKOFF_MILLIS).

  • Zasada ponawiania określa, jak opóźnienie do ponowienia powinno się zwiększać w przypadku kolejnych prób ponowienia. WorkManager obsługuje 2 zasady wycofywania: LINEAR i EXPONENTIAL.

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 minimalne opóźnienie do ponowienia jest ustawione na minimalną dopuszczalne wartości, czyli 10 sekund. Ponieważ zasada ma wartość LINEAR, przy każdej nowej próbie interwał ponawiania prób zwiększa się o około 10 sekund. Na przykład pierwsze uruchomienie, które kończy się poleceniem Result.retry(), zostanie ponowione po 10 sekundach, kolejne 20, 30, 40 itd., jeśli po kolejnych próbach działanie będzie nadal zwracać wartość Result.retry(). Jeśli zasada ponowienia ma wartość EXPONENTIAL, sekwencja czasu ponawiania prób będzie zbliżona do 20, 40, 80 itd.

Tag działa

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ę powiązanych logicznie zadań, pomocne może być też ich otagowanie. Tagowanie pozwala na współdziałanie z grupą żądań roboczych.

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 roboczego można dodać wiele tagów. Wewnętrznie tagi te są przechowywane jako zbiór ciągów znaków. Aby uzyskać zbiór tagów powiązanych z WorkRequest, możesz użyć WorkInfo.getTags().

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

Przypisz dane wejściowe

Twoja praca może wymagać danych wejściowych. Na przykład moduły do przesyłania obrazu mogą wymagać przesłania 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 udostępni dane wejściowe Data podczas wykonywania pracy. Klasa Worker ma dostęp do argumentów wejściowych, wywołując Worker.getInputData(). Poniższy kod pokazuje, jak utworzyć instancję Worker, która wymaga danych wejściowych, i jak je wysyłać w żądaniu roboczym.

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

Analogicznie klasy Data można użyć do wygenerowania zwracanej wartości. Więcej informacji o danych wejściowych i wyjściowych znajdziesz w sekcji Parametry wejściowe i zwracane wartości.

Dalsze kroki

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