Android Kotlin Fundamentals 09.2: WorkManager

Ten moduł Codelab jest częścią kursu Android Kotlin Fundamentals. Najwięcej korzyści przyniesie Ci ukończenie wszystkich ćwiczeń w kolejności. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej kursu Android Kotlin Fundamentals.

Wprowadzenie

Większość aplikacji w rzeczywistym świecie musi wykonywać długotrwałe zadania w tle. Na przykład aplikacja może przesyłać pliki na serwer, synchronizować dane z serwera i zapisywać je w Roombazie danych, wysyłać dzienniki na serwer lub wykonywać złożone operacje na danych. Takie operacje powinny być wykonywane w tle, poza wątkiem interfejsu (wątkiem głównym). Zadania w tle zużywają ograniczone zasoby urządzenia, takie jak pamięć RAM i bateria. Jeśli nie zostanie to odpowiednio obsłużone, może negatywnie wpłynąć na wygodę użytkowników.

Z tego modułu dowiesz się, jak używać WorkManager do planowania zadań w tle w zoptymalizowany i wydajny sposób. Więcej informacji o innych dostępnych rozwiązaniach do przetwarzania w tle na Androidzie znajdziesz w przewodniku po przetwarzaniu w tle.

Co warto wiedzieć

  • Jak korzystać z komponentów architektury Androida ViewModel, LiveDataRoom.
  • Jak przeprowadzać przekształcenia w klasie LiveData.
  • Jak utworzyć i uruchomić korutynę.
  • Jak używać adapterów powiązań w powiązaniu danych.
  • Jak wczytywać dane z pamięci podręcznej za pomocą wzorca repozytorium.

Czego się nauczysz

  • Jak utworzyć Worker, czyli jednostkę pracy.
  • Jak utworzyć WorkRequest, aby poprosić o wykonanie pracy.
  • Jak dodać ograniczenia do elementu WorkRequest, aby określić, jak i kiedy ma działać pracownik.
  • Jak używać WorkManager do planowania zadań w tle.

Jakie zadania wykonasz

  • Utwórz proces roboczy, który będzie wykonywać zadanie w tle polegające na wstępnym pobieraniu z sieci playlisty filmów DevBytes.
  • Zaplanuj okresowe uruchamianie instancji roboczej.
  • Dodaj ograniczenia do WorkRequest.
  • Zaplanuj okresowe WorkRequest, które będzie wykonywane raz dziennie.

W tym ćwiczeniu z programowania będziesz pracować nad aplikacją DevBytes, którą utworzono w poprzednim ćwiczeniu. (Jeśli nie masz tej aplikacji, możesz pobrać kod początkowy do tej lekcji).

Aplikacja DevBytes wyświetla listę filmów DevByte, czyli krótkich samouczków przygotowanych przez zespół ds. relacji z deweloperami Androida w Google. Filmy te przedstawiają funkcje dla programistów i sprawdzone metody tworzenia aplikacji na Androida.

Ulepszasz wrażenia użytkowników w aplikacji, wstępnie pobierając filmy raz dziennie. Dzięki temu użytkownik zobaczy aktualne treści od razu po otwarciu aplikacji.

W tym zadaniu pobierzesz i sprawdzisz kod startowy.

Krok 1. Pobierz i uruchom aplikację startową

Możesz kontynuować pracę nad aplikacją DevBytes, którą utworzono w poprzednim laboratorium (jeśli ją masz). Możesz też pobrać aplikację startową.

W tym zadaniu pobierzesz i uruchomisz aplikację startową oraz sprawdzisz kod początkowy.

  1. Jeśli nie masz jeszcze aplikacji DevBytes, pobierz kod początkowy DevBytes do tego laboratorium z kodu z projektu DevBytesRepository w GitHubie.
  2. Rozpakuj kod i otwórz projekt w Android Studio.
  3. Połącz urządzenie testowe lub emulator z internetem, jeśli nie jest jeszcze połączone. Skompiluj i uruchom aplikację. Aplikacja pobierze z sieci listę filmów DevByte i wyświetli je.
  4. W aplikacji kliknij dowolny film, aby otworzyć go w aplikacji YouTube.

Krok 2. Zapoznaj się z kodem

Aplikacja początkowa zawiera dużo kodu, który został wprowadzony w poprzednim ćwiczeniu. Kod początkowy do tych ćwiczeń z programowania zawiera moduły sieciowe, interfejsu użytkownika, pamięci podręcznej offline i repozytorium. Możesz skupić się na planowaniu zadania w tle za pomocą funkcji WorkManager.

  1. W Android Studio rozwiń wszystkie pakiety.
  2. Zapoznaj się z pakietem database. Pakiet zawiera jednostki bazy danych i lokalną bazę danych, która jest zaimplementowana przy użyciu Room.
  3. Zapoznaj się z pakietem repository. Pakiet zawiera klasę VideosRepository, która oddziela warstwę danych od reszty aplikacji.
  4. Pozostałą część kodu początkowego możesz sprawdzić samodzielnie, korzystając z pomocy poprzedniego laboratorium.

WorkManager jest jednym ze składników architektury Androida i częścią Androida Jetpack. WorkManager – w przypadku zadań w tle, które można odłożyć i które wymagają gwarantowanego wykonania:

  • Odroczenie oznacza, że zadanie nie musi być wykonane natychmiast. Na przykład wysyłanie danych analitycznych na serwer lub synchronizowanie bazy danych w tle to działania, które można odłożyć w czasie.
  • Gwarantowane wykonanie oznacza, że zadanie zostanie wykonane nawet wtedy, gdy aplikacja zostanie zamknięta lub urządzenie zostanie ponownie uruchomione.

Gdy WorkManager działa w tle, rozwiązuje problemy z kompatybilnością i stosuje sprawdzone metody dotyczące baterii i stanu systemu. WorkManager jest zgodny z poziomem API 14. WorkManager wybiera odpowiedni sposób planowania zadania w tle w zależności od poziomu interfejsu API urządzenia. Może używać JobScheduler (w przypadku interfejsu API w wersji 23 i nowszych) lub kombinacji AlarmManagerBroadcastReceiver.

WorkManager umożliwia też ustawienie kryteriów, kiedy zadanie w tle ma być uruchamiane. Możesz na przykład chcieć, aby zadanie było wykonywane tylko wtedy, gdy stan baterii, stan sieci lub stan ładowania spełniają określone kryteria. W dalszej części tego modułu dowiesz się, jak ustawiać ograniczenia.

W tym ćwiczeniu w Codelabs zaplanujesz zadanie, które będzie codziennie pobierać z sieci listę odtwarzania filmów DevBytes. Aby zaplanować to zadanie, użyj biblioteki WorkManager.

  1. Otwórz plik build.gradle (Module:app) i dodaj do projektu zależność WorkManager.

    Jeśli używasz najnowszej wersji biblioteki, aplikacja z rozwiązaniem powinna się skompilować zgodnie z oczekiwaniami. Jeśli nie, spróbuj rozwiązać problem lub wróć do wersji biblioteki podanej poniżej.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. Zsynchronizuj projekt i upewnij się, że nie ma błędów kompilacji.

Zanim dodasz kod do projektu, zapoznaj się z tymi klasami w bibliotece WorkManager:

  • Worker
     W tej klasie definiujesz rzeczywistą pracę (zadanie), która ma być wykonywana w tle. Rozszerzasz tę klasę i zastępujesz metodę doWork(). Metoda doWork() to miejsce, w którym umieszczasz kod do wykonania w tle, np. synchronizację danych z serwerem lub przetwarzanie obrazów. W tym zadaniu wdrażasz Worker.
  • WorkRequest
    Ta klasa reprezentuje prośbę o uruchomienie procesu roboczego w tle. Użyj WorkRequest, aby skonfigurować sposób i czas uruchamiania zadania roboczego za pomocą Constraints, takich jak podłączenie urządzenia do zasilania lub połączenie z Wi-Fi. WorkRequest zaimplementujesz w późniejszym zadaniu.
  • WorkManager
     Ta klasa planuje i uruchamia WorkRequest. WorkManager planuje żądania pracy w taki sposób, aby rozłożyć obciążenie zasobów systemowych, przy jednoczesnym uwzględnieniu określonych przez Ciebie ograniczeń. WorkManager zaimplementujesz w późniejszym zadaniu.

Krok 1. Utwórz pracownika

W tym zadaniu dodasz element Worker, aby wstępnie pobrać w tle playlistę filmów DevBytes.

  1. W pakiecie devbyteviewer utwórz nowy pakiet o nazwie work.
  2. W pakiecie work utwórz nową klasę Kotlin o nazwie RefreshDataWorker.
  3. Rozszerz klasę RefreshDataWorker z klasy CoroutineWorker. Przekaż contextWorkerParameters jako parametry konstruktora.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. Aby rozwiązać błąd klasy abstrakcyjnej, zastąp metodę doWork() w klasie RefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}

Funkcja zawieszająca to funkcja, którą można wstrzymać i wznowić później. Funkcja zawieszająca może wykonywać długotrwałą operację i czekać na jej zakończenie bez blokowania wątku głównego.

Krok 2. Zaimplementuj funkcję doWork()

Metoda doWork() w klasie Worker jest wywoływana w wątku w tle. Metoda wykonuje pracę synchronicznie i powinna zwracać obiekt ListenableWorker.Result. System Android daje Worker maksymalnie 10 minut na zakończenie wykonywania i zwrócenie obiektu ListenableWorker.Result. Po upływie tego czasu system wymusza zatrzymanie Worker.

Aby utworzyć obiekt ListenableWorker.Result, wywołaj jedną z tych metod statycznych, aby wskazać stan ukończenia pracy w tle:

W tym zadaniu zaimplementujesz metodę doWork(), aby pobrać z sieci playlistę filmów DevBytes. Możesz ponownie wykorzystać istniejące metody w klasie VideosRepository, aby pobrać dane z sieci.

  1. W klasie RefreshDataWorker wewnątrz doWork() utwórz i zainicjuj obiekt VideosDatabase oraz obiekt VideosRepository.
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. W klasie RefreshDataWorker, wewnątrz doWork(), nad instrukcją return wywołaj metodę refreshVideos() w bloku try. Dodaj log, aby śledzić, kiedy instancja robocza jest uruchamiana.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

Aby rozwiązać błąd „Unresolved reference” (Nierozwiązane odwołanie), zaimportuj retrofit2.HttpException.

  1. Oto pełna klasa RefreshDataWorker:
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

Worker definiuje jednostkę pracy, a WorkRequest określa, jak i kiedy należy wykonać pracę. Istnieją 2 konkretne implementacje klasy WorkRequest:

  • Klasa OneTimeWorkRequest jest przeznaczona do zadań jednorazowych. (Jednorazowe zadanie jest wykonywane tylko raz).
  • Klasa PeriodicWorkRequest jest przeznaczona do pracy okresowej, która powtarza się w określonych odstępach czasu.

Zadania mogą być jednorazowe lub okresowe, więc wybierz odpowiednie zajęcia. Więcej informacji o planowaniu powtarzających się zadań znajdziesz w dokumentacji dotyczącej powtarzających się zadań.

W tym zadaniu zdefiniujesz i zaplanujesz WorkRequest, aby uruchomić proces roboczy utworzony w poprzednim zadaniu.

Krok 1. Skonfiguruj powtarzające się zadania

W aplikacji na Androida klasa Application jest klasą bazową, która zawiera wszystkie inne komponenty, takie jak aktywności i usługi. Gdy proces dotyczący aplikacji lub pakietu zostanie utworzony, klasa Application (lub dowolna podklasa klasy Application) jest tworzona przed każdą inną klasą.

W tej przykładowej aplikacji klasa DevByteApplication jest podklasą klasy Application. DevByteApplication zajęcia to dobre miejsce na zaplanowanie WorkManager.

  1. W klasie DevByteApplication utwórz metodę o nazwie setupRecurringWork(), aby skonfigurować cykliczne zadanie w tle.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. W metodzie setupRecurringWork() utwórz i zainicjuj okresowe żądanie pracy, które będzie wykonywane raz dziennie, używając metody PeriodicWorkRequestBuilder(). Przekaż klasę RefreshDataWorker utworzoną w poprzednim zadaniu. Przekaż interwał powtarzania 1 z jednostką czasu TimeUnit.DAYS.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

Aby rozwiązać ten problem, zaimportuj java.util.concurrent.TimeUnit.

Krok 2. Zaplanuj WorkRequest za pomocą WorkManager

Po zdefiniowaniu WorkRequest możesz zaplanować ją za pomocą WorkManager, używając metody enqueueUniquePeriodicWork(). Ta metoda umożliwia dodanie do kolejki unikalnie nazwanego elementu PeriodicWorkRequest, przy czym w danym momencie może być aktywny tylko jeden element PeriodicWorkRequest o określonej nazwie.

Możesz na przykład chcieć, aby aktywna była tylko jedna operacja synchronizacji. Jeśli oczekuje na wykonanie jedna operacja synchronizacji, możesz zezwolić na jej wykonanie lub zastąpić ją nową pracą, używając ExistingPeriodicWorkPolicy.

Więcej informacji o sposobach planowania WorkRequest znajdziesz w dokumentacji WorkManager.

  1. W klasie RefreshDataWorker na początku klasy dodaj obiekt towarzyszący. Określ nazwę pracownika, aby jednoznacznie go zidentyfikować.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. W klasie DevByteApplication na końcu metody setupRecurringWork() zaplanuj pracę za pomocą metody enqueueUniquePeriodicWork(). Przekaż enum KEEP dla ExistingPeriodicWorkPolicy. Przekaż repeatingRequest jako parametr PeriodicWorkRequest.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

Jeśli istnieje oczekująca (nieukończona) praca o tej samej nazwie, parametr ExistingPeriodicWorkPolicy.KEEP sprawia, że WorkManager zachowuje poprzednią pracę okresową i odrzuca nowe żądanie pracy.

  1. Na początku klasy DevByteApplication utwórz obiekt CoroutineScope. Przekaż Dispatchers.Default jako parametr konstruktora.
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. W klasie DevByteApplication dodaj nową metodę o nazwie delayedInit(), aby uruchomić korutynę.
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. W metodzie delayedInit() wywołaj metodę setupRecurringWork().
  2. Przenieś inicjowanie Timber z metody onCreate() do metody delayedInit().
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. W klasie DevByteApplication na końcu metody onCreate() dodaj wywołanie metody delayedInit().
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. Otwórz panel Logcat u dołu okna Android Studio. Filtruj według: RefreshDataWorker.
  2. Uruchom aplikację. WorkManager natychmiast zaplanuje Twoją cykliczną pracę.

    W panelu Logcat zwróć uwagę na instrukcje logowania, które pokazują, że żądanie wykonania zostało zaplanowane, a następnie wykonane.
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

WM-WorkerWrapper log jest wyświetlany z WorkManager biblioteki, więc nie możesz zmienić tego komunikatu.

Krok 3. (Opcjonalnie) Zaplanuj WorkRequest na minimalny interwał

W tym kroku zmniejszysz przedział czasu z 1 dnia do 15 minut. Dzięki temu możesz zobaczyć logi okresowego żądania pracy w działaniu.

  1. W klasie DevByteApplication w metodzie setupRecurringWork() zakomentuj bieżącą definicję repeatingRequest. Dodaj nową prośbę o wykonanie pracy z okresowym interwałem powtarzania wynoszącym 15 minut.
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. Otwórz panel Logcat w Android Studio i filtruj według RefreshDataWorker. Aby wyczyścić poprzednie dzienniki, kliknij ikonę Wyczyść Logcat  .
  2. Uruchom aplikację, a WorkManager od razu zaplanuje Twoją cykliczną pracę. W panelu Logcat zwróć uwagę na dzienniki – żądanie pracy jest uruchamiane co 15 minut. Poczekaj 15 minut, aby zobaczyć kolejny zestaw logów żądań pracy. Możesz pozostawić aplikację uruchomioną lub ją zamknąć. Menedżer zadań powinien nadal działać.

    Zwróć uwagę, że interwał jest czasami krótszy niż 15 minut, a czasami dłuższy. (Dokładny czas zależy od optymalizacji baterii w systemie operacyjnym).
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

Gratulacje! Utworzono pracownika i zaplanowano prośbę o wykonanie pracy w usłudze WorkManager. Ale jest problem: nie podano żadnych ograniczeń. WorkManager zaplanuje pracę raz dziennie, nawet jeśli bateria urządzenia jest słaba, urządzenie jest w trybie uśpienia lub nie ma połączenia z siecią. Wpłynie to na baterię i wydajność urządzenia oraz może pogorszyć wygodę użytkowników.

W kolejnym zadaniu rozwiążesz ten problem, dodając ograniczenia.

W poprzednim zadaniu użyto ikony WorkManager do zaplanowania prośby o wykonanie pracy. W tym zadaniu dodasz kryteria określające, kiedy ma być wykonywana praca.

Podczas definiowania WorkRequest możesz określić ograniczenia dotyczące tego, kiedy Worker ma być uruchamiana. Możesz na przykład określić, że zadanie ma być wykonywane tylko wtedy, gdy urządzenie jest nieaktywne lub gdy jest podłączone do zasilania i połączone z Wi-Fi. Możesz też określić zasady wycofywania, aby ponawiać wykonywanie zadań. Obsługiwane ograniczenia to metody ustawione w Constraints.Builder. Więcej informacji znajdziesz w artykule Określanie próśb o wykonanie pracy.

Krok 1. Dodaj obiekt Constraints i ustaw jedno ograniczenie

W tym kroku utworzysz obiekt Constraints i ustawisz na nim jedno ograniczenie – ograniczenie typu sieci. (Łatwiej jest zauważyć logi z tylko jednym ograniczeniem. W dalszej części dodasz inne ograniczenia).

  1. W klasie DevByteApplication na początku setupRecurringWork() zdefiniuj val typu Constraints. Użyj metody Constraints.Builder().
val constraints = Constraints.Builder()

Aby rozwiązać ten problem, zaimportuj androidx.work.Constraints.

  1. Użyj metody setRequiredNetworkType(), aby dodać do obiektu constraints ograniczenie typu sieć. Użyj wyliczenia UNMETERED, aby żądanie pracy było wykonywane tylko wtedy, gdy urządzenie jest połączone z siecią bez limitu danych.
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. Użyj metody build(), aby wygenerować ograniczenia z konstruktora.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

Teraz musisz ustawić nowo utworzony obiekt Constraints w żądaniu pracy.

  1. W klasie DevByteApplication w metodzie setupRecurringWork() ustaw obiekt Constraints na okresowe żądanie wykonania zadania repeatingRequest. Aby ustawić ograniczenia, dodaj metodę setConstraints() nad wywołaniem metody build().
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

Krok 2. Uruchom aplikację i zwróć uwagę na dzienniki

W tym kroku uruchomisz aplikację i zauważysz, że ograniczone żądanie pracy jest uruchamiane w tle w określonych odstępach czasu.

  1. Aby anulować wcześniej zaplanowane zadania, odinstaluj aplikację z urządzenia lub emulatora.
  2. Otwórz panel Logcat w Android Studio. W panelu Logcat wyczyść poprzednie logi, klikając ikonę Wyczyść Logcat  po lewej stronie. Filtruj według: work.
  3. Wyłącz Wi-Fi na urządzeniu lub emulatorze, aby zobaczyć, jak działają ograniczenia. Obecny kod ustawia tylko jedno ograniczenie, które wskazuje, że żądanie powinno być uruchamiane tylko w sieci bez limitu danych. Wi-Fi jest wyłączone, więc urządzenie nie jest połączone z siecią (ani z siecią rozliczaną, ani z siecią nierozliczaną). Dlatego to ograniczenie nie zostanie spełnione.
  4. Uruchom aplikację i zwróć uwagę na panel Logcat. Usługa WorkManager natychmiast planuje zadanie w tle. Zadanie nie zostanie uruchomione, ponieważ nie spełnia ograniczeń sieciowych.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. Włącz Wi-Fi na urządzeniu lub emulatorze i obserwuj panel Logcat. Zaplanowane zadanie w tle jest teraz uruchamiane co około 15 minut, o ile spełnione jest ograniczenie dotyczące sieci.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

Krok 3. Dodaj więcej ograniczeń

W tym kroku dodasz do pliku PeriodicWorkRequest te ograniczenia:

  • Bateria nie jest słaba.
  • ładowanie urządzenia,
  • Urządzenie jest nieaktywne; dostępne tylko na poziomie interfejsu API 23 (Android M) i nowszym.

Zaimplementuj w klasie DevByteApplication te elementy:

  1. W klasie DevByteApplication w metodzie setupRecurringWork() wskaż, że żądanie pracy powinno być uruchamiane tylko wtedy, gdy poziom baterii nie jest niski. Dodaj ograniczenie przed wywołaniem metody build() i użyj metody setRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)
  1. Zaktualizuj żądanie pracy, aby było wykonywane tylko wtedy, gdy urządzenie jest ładowane. Dodaj ograniczenie przed wywołaniem metody build() i użyj metody setRequiresCharging().
.setRequiresCharging(true)
  1. Zaktualizuj żądanie pracy, aby było wykonywane tylko wtedy, gdy urządzenie jest bezczynne. Dodaj ograniczenie przed wywołaniem metody build() i użyj metody setRequiresDeviceIdle(). To ograniczenie powoduje uruchomienie żądania pracy tylko wtedy, gdy użytkownik nie używa aktywnie urządzenia. Ta funkcja jest dostępna tylko na Androidzie 6.0 (Marshmallow) i nowszym, więc dodaj warunek dla wersji pakietu SDK M lub nowszej.
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

Oto pełna definicja obiektu constraints.

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. W metodzie setupRecurringWork() zmień interwał żądania z powrotem na raz dziennie.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

Oto pełna implementacja metody setupRecurringWork() wraz z dziennikiem, który umożliwia śledzenie, kiedy zaplanowano okresowe żądanie wykonania zadania.

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. Aby usunąć wcześniej zaplanowaną prośbę o wykonanie pracy, odinstaluj aplikację DevBytes z urządzenia lub emulatora.
  2. Uruchom aplikację, a WorkManager natychmiast zaplanuje prośbę o wykonanie zadania. Żądanie pracy jest wykonywane raz dziennie, gdy zostaną spełnione wszystkie ograniczenia.
  3. To żądanie będzie działać w tle, dopóki aplikacja jest zainstalowana, nawet jeśli nie jest uruchomiona. Z tego powodu należy odinstalować aplikację z telefonu.

Doskonale! W aplikacji DevBytes zaimplementowano i zaplanowano przyjazne dla baterii żądanie pracy dotyczące codziennego wstępnego pobierania filmów. WorkManager zaplanuje i wykona pracę, optymalizując zasoby systemu. Twoi użytkownicy i ich baterie będą bardzo zadowoleni.

Projekt Android Studio: DevBytesWorkManager.

  • Interfejs WorkManager API ułatwia planowanie odroczonych zadań asynchronicznych, które muszą być wykonywane niezawodnie.
  • Większość aplikacji w rzeczywistym świecie musi wykonywać długotrwałe zadania w tle. Aby zaplanować zadanie w tle w zoptymalizowany i wydajny sposób, użyj WorkManager.
  • Główne klasy w bibliotece WorkManager to Worker, WorkRequestWorkManager.
  • Klasa Worker reprezentuje jednostkę pracy. Aby zaimplementować zadanie w tle, rozszerz klasę Worker i zastąp metodę doWork().
  • Klasa WorkRequest reprezentuje prośbę o wykonanie jednostki pracy. WorkRequest to klasa bazowa do określania parametrów pracy, którą planujesz w WorkManager.
  • Klasa WorkRequest ma 2 konkretne implementacje: OneTimeWorkRequest w przypadku jednorazowych zadań i PeriodicWorkRequest w przypadku okresowych żądań pracy.
  • Podczas definiowania WorkRequest możesz określić Constraints, aby wskazać, kiedy ma się uruchamiać Worker. Ograniczenia obejmują m.in. to, czy urządzenie jest podłączone do zasilania, czy jest w stanie bezczynności oraz czy jest połączone z Wi-Fi.
  • Aby dodać ograniczenia do elementu WorkRequest, użyj metod ustawiania wymienionych w dokumentacji Constraints.Builder. Jeśli na przykład chcesz wskazać, że WorkRequest nie powinien działać, gdy bateria urządzenia jest bliska rozładowania, użyj metody setRequiresBatteryNotLow().
  • Po zdefiniowaniu WorkRequest przekaż zadanie do systemu Android. Aby to zrobić, zaplanuj zadanie za pomocą jednej z WorkManager enqueue metod.
  • Dokładny czas wykonania Worker zależy od ograniczeń użytych w WorkRequest i od optymalizacji systemu. WorkManager ma zapewniać najlepsze możliwe działanie w ramach tych ograniczeń.

Kurs Udacity:

Dokumentacja dla deweloperów aplikacji na Androida:

Inne:

W tej sekcji znajdziesz listę możliwych zadań domowych dla uczniów, którzy wykonują ten moduł w ramach kursu prowadzonego przez instruktora. Nauczyciel musi:

  • W razie potrzeby przypisz pracę domową.
  • Poinformuj uczniów, jak przesyłać projekty.
  • Oceń zadania domowe.

Instruktorzy mogą korzystać z tych sugestii w dowolnym zakresie i mogą zadawać inne zadania domowe, które uznają za odpowiednie.

Jeśli wykonujesz ten kurs samodzielnie, możesz użyć tych zadań domowych, aby sprawdzić swoją wiedzę.

Pytanie 1

Jakie są konkretne implementacje klasy WorkRequest?

▢ OneTimeWorkPeriodicRequest

▢ OneTimeWorkRequestPeriodicWorkRequest

▢ OneTimeWorkRequestRecurringWorkRequest

▢ OneTimeOffWorkRequestRecurringWorkRequest

Pytanie 2

Której z poniższych klas używa WorkManager do planowania zadania w tle na poziomie API 23 i wyższym?

▢ Tylko JobScheduler

▢ BroadcastReceiverAlarmManager

▢ AlarmManagerJobScheduler

▢ SchedulerBroadcastReceiver

Pytanie 3

Którego interfejsu API używasz, aby dodać ograniczenia do WorkRequest?

▢ setConstraints()

▢ addConstraints()

▢ setConstraint()

▢ addConstraintsToWorkRequest()

Przejdź do następnej lekcji: 10.1 Style i motywy

Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.