Android Kotlin Fundamentals 09.2: WorkManager

Te ćwiczenia są częścią kursu Android Kotlin Fundamentals. Skorzystaj z tego kursu, jeśli będziesz wykonywać kolejno kilka ćwiczeń z programowania. Wszystkie ćwiczenia z kursu są wymienione na stronie docelowej ćwiczeń z programowania na temat Kotlin.

Wstęp

Większość rzeczywistych aplikacji wymaga wykonywania długotrwałych zadań w tle. Aplikacja może na przykład przesyłać pliki na serwer, synchronizować dane z serwera i zapisywać je w bazie danych Room, wysyłać dzienniki na serwer oraz wykonywać kosztowne operacje na danych. Takie operacje należy wykonywać w tle, poza wątkiem interfejsu (główny wątek). Zadania w tle zużywają ograniczone zasoby urządzenia, takie jak pamięć RAM i bateria. Może to negatywnie wpłynąć na wrażenia użytkownika.

Z tego ćwiczenia dowiesz się, jak używać narzędzia WorkManager, by planować zadania w tle w sposób zoptymalizowany i wydajny. Więcej informacji o innych rozwiązaniach umożliwiających przetwarzanie w tle znajdziesz w artykule Przewodnik po przetwarzaniu w tle.

Co musisz wiedzieć

  • Używanie elementów ViewModel, LiveData i Room komponentów architektury Androida.
  • Jak przeprowadzać przekształcenia w klasie LiveData.
  • Jak utworzyć i uruchomić program.
  • Jak używać adapterów do wiązania danych.
  • Jak wczytać dane z pamięci podręcznej za pomocą wzorca repozytorium.

Czego się nauczysz

  • Jak utworzyć Worker, który reprezentuje jednostkę pracy.
  • Jak utworzyć WorkRequest, aby poprosić o wykonanie zadania.
  • Dowiedz się, jak dodać ograniczenia do WorkRequest, by określić sposób i czas działania instancji roboczej.
  • Jak używać WorkManager do planowania zadań w tle.

Jakie zadania wykonasz:

  • Utwórz węzeł roboczy, aby wykonać zadanie w tle, aby wstępnie pobrać playlistę filmów DevBytes z sieci.
  • Zaplanuj okresowe uruchamianie instancji roboczej.
  • Dodaj ograniczenia do WorkRequest.
  • Zaplanuj okresowe przeprowadzanie WorkRequest w ciągu dnia.

W tym ćwiczeniu będziesz pracować nad aplikacją DevBytes utworzoną w poprzednim ćwiczeniu. (Jeśli nie masz tej aplikacji, możesz pobrać kod startowy lekcji).

Aplikacja DevBytes zawiera listę filmów w DevByte, czyli krótkich samouczków przygotowanych przez zespół Google Developers ds. relacji z deweloperami. Filmy zawierają funkcje dla programistów i sprawdzone metody dotyczące ich programowania.

Aby zwiększyć komfort użytkowników aplikacji, pobieraj filmy raz na dzień. Dzięki temu użytkownik ma dostęp do nowych treści zaraz po otwarciu aplikacji.

W tym zadaniu pobierzesz i przejrzysz kod startowy.

Krok 1. Pobierz i uruchom aplikację startową

Możesz dalej pracować w aplikacji DevBytes, która jest dostępna w Twoim poprzednim kursie z programowania (jeśli go masz). Możesz też pobrać aplikację startową.

W tym zadaniu pobierzesz i uruchomisz aplikację startową, a następnie sprawdzisz jej kod.

  1. Jeśli nie masz jeszcze aplikacji DevBytes, pobierz kod startowy DevBytes dla tego ćwiczenia z programowania z projektu DevBytesRepository z GitHuba.
  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łączony. Utwórz i uruchom aplikację. Ta aplikacja pobiera listę filmów DevByte z sieci i wyświetla je.
  4. W aplikacji kliknij dowolny film, aby otworzyć go w aplikacji YouTube.

Krok 2. Poznaj kod

Aplikacja ta zawiera wiele kodów wprowadzonych w poprzednim ćwiczeniu z programowania. Kod startowy tego ćwiczenia z programowania zawiera moduły sieciowe, interfejs, pamięć podręczną offline i repozytorium. Możesz zaplanować działanie zadania w tle przy użyciu WorkManager.

  1. W Android Studio rozwiń wszystkie pakiety.
  2. Poznaj pakiet database Pakiet zawiera elementy bazy danych i lokalną bazę danych, która jest zaimplementowana za pomocą funkcji Room.
  3. Poznaj pakiet repository Pakiet zawiera klasę VideosRepository, która wyodrębnia warstwę danych od reszty aplikacji.
  4. Zapoznaj się z pozostałymi fragmentami kodu startowego samodzielnie i skorzystaj z wcześniejszego ćwiczenia z programowania.

WorkManager jest jednym z komponentów architektury Androida i jest częścią pakietu Jetpack na Androida. WorkManager oznacza prace w tle, które można odroczyć i wymagają gwarantowanego wykonania:

  • Możliwe do odczytania oznacza, że zadanie nie musi być uruchamiane natychmiast. Na przykład wysyłanie danych analitycznych do serwera lub synchronizowanie bazy danych w tle może być opóźnione.
  • Gwarantowane wykonanie oznacza, że zadanie będzie uruchamiane nawet po zamknięciu aplikacji lub ponownym uruchomieniu urządzenia.

WorkManager uruchamia prace w tle, ale rozwiązuje problemy ze zgodnością i sprawdzone metody dotyczące kondycji baterii i systemu. WorkManager zapewnia zgodność z interfejsem API na poziomie 14. WorkManager wybiera odpowiedni sposób planowania zadania w tle w zależności od poziomu interfejsu API urządzenia. Może korzystać z metody JobScheduler (interfejs API 23 i nowsze) lub kombinacji funkcji AlarmManager i BroadcastReceiver.

WorkManager umożliwia też określenie kryteriów uruchamiania zadania w tle. Może być na przykład konieczne, aby zadanie działało tylko wtedy, gdy stan baterii, stan sieci lub stan ładowania spełniają określone kryteria. W dalszej części tego ćwiczenia dowiesz się, jak określać ograniczenia.

W tym ćwiczeniu zaplanujesz zadanie pobierania raz dziennie playlisty wideo DevBytes z sieci. Aby zaplanować to zadanie, użyj biblioteki WorkManager.

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

    Jeśli używasz najnowszej wersji biblioteki, aplikacja z rozwiązaniami powinna skompilować się w oczekiwany sposób. Jeśli tak się nie stanie, spróbuj rozwiązać problem lub przywrócić wersję biblioteki widoczną poniżej.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. Zsynchronizuj swój projekt i upewnij się, że nie ma w nim błędów kompilacji.

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

  • Worker
    Na tych zajęciach definiujesz rzeczywiste zadania (zadanie), które mają być wykonywane w tle. Wydłużasz tę klasę i zastępujesz metodę doWork(). Metoda doWork() to kod, który umieszczasz w tle, aby np. synchronizować dane z serwerem lub przetwarzać obrazy. W tym zadaniu stosujesz Worker.
  • WorkRequest
    Ta klasa reprezentuje żądanie uruchomienia instancji roboczej w tle. Użyj metody WorkRequest, aby skonfigurować sposób i czas wykonywania zadania, korzystając z Constraints, na przykład podłączonego urządzenia lub podłączonego do Wi-Fi. Implementację funkcji WorkRequest przeprowadzasz w późniejszym zadaniu.
  • WorkManager
    Te zajęcia będą zaplanowane i zaplanowane na WorkRequest. WorkManager planuje zadania w sposób, który rozkłada obciążenie zasobów systemowych i jednocześnie spełnia określone przez Ciebie ograniczenia. Implementację funkcji WorkManager przeprowadzasz w późniejszym zadaniu.

Krok 1. Utwórz węzeł roboczy

W tym zadaniu dodasz Worker, aby pobierać w tle tło playlisty 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. Wydłuż klasę RefreshDataWorker z klasy CoroutineWorker. Przekaż context i WorkerParameters jako parametry konstruktora.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. Aby naprawić błąd abstrakcyjnej klasy, zastąp metodę doWork() w klasie RefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}

Funkcja zawieszenia to funkcja, którą można wstrzymać i wznowić później. Zawieszona funkcja może wykonać długo trwające działanie i poczekać na zakończenie operacji bez blokowania głównego wątku.

Krok 2. Zaimplementuj dowork()

Metoda doWork() wewnątrz klasy Worker jest wywoływana w wątku w tle. Ta metoda działa synchronicznie i zwraca obiekt ListenableWorker.Result. System Android daje Worker maksymalnie 10 minut na dokończenie wykonania 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, by wskazać stan ukończenia zadania w tle:

W tym zadaniu implementujesz metodę doWork(), która służy do pobierania playlisty wideo DevBytes z sieci. Aby pobrać dane z sieci, możesz ponownie użyć istniejących metod z klasy VideosRepository.

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

   return Result.success()
}
  1. W klasie RefreshDataWorker wewnątrz tagu doWork(), nad instrukcją return wywołaj metodę refreshVideos() wewnątrz bloku try. Dodaj dziennik do śledzenia uruchomionej instancji roboczej.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

Aby naprawić błąd &"Nierozwiązany problem z plikiem referencyjnym, 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 określa jednostkę pracy, a WorkRequest określa sposób i czas uruchamiania zadania. Są 2 konkretne implementacje klasy WorkRequest:

  • Klasa OneTimeWorkRequest służy do jednorazowych zadań. Zadanie jednorazowe ma miejsce tylko raz.
  • Klasa PeriodicWorkRequest dotyczy zadań cyklicznych, które powtarzają się w określonych odstępach czasu.

Zadania mogą być jednorazowe lub okresowe, więc wybierz odpowiednie zajęcia. Więcej informacji o harmonogramie zadań cyklicznych znajdziesz w dokumentacji zadań cyklicznych.

W tym zadaniu definiujesz i uruchamiasz uruchomienie WorkRequest instancji roboczej utworzonej w poprzednim zadaniu.

Krok 1. Skonfiguruj zadanie cykliczne

Klasa Application w aplikacji na Androida to klasa podstawowa zawierająca wszystkie inne komponenty, takie jak aktywności i usługi. Gdy zostanie utworzony proces aplikacji lub pakietu, klasa Application (lub dowolna podklasa Application) jest tworzona przed innymi klasami.

W przykładowej aplikacji klasa DevByteApplication jest podklasą klasy Application. Klasa DevByteApplication to dobre miejsce na zaplanowanie spotkania WorkManager.

  1. W klasie DevByteApplication utwórz metodę o nazwie setupRecurringWork(), aby skonfigurować zadanie cykliczne w tle.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. W ramach metody setupRecurringWork() utwórz i zainicjuj okresowe żądanie zadania do uruchomienia raz dziennie, korzystając z metody PeriodicWorkRequestBuilder(). Przekaż klasę RefreshDataWorker utworzoną w poprzednim zadaniu. Powtarzaj przedział czasu 1 za pomocą jednostki czasu TimeUnit.DAYS.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

Aby naprawić ten błąd, zaimportuj java.util.concurrent.TimeUnit.

Krok 2. Zaplanuj żądanie w WorkManager

Po zdefiniowaniu elementu WorkRequest możesz go zaplanować za pomocą metody WorkManager, korzystając z metody enqueueUniquePeriodicWork(). Ta metoda pozwala dodać do kolejki unikalną nazwę PeriodicWorkRequest, w której tylko 1 element PeriodicWorkRequest o określonej nazwie może być aktywny.

Możesz na przykład chcieć, aby tylko jedna operacja synchronizacji była aktywna. Jeśli jakaś operacja synchronizacji oczekuje na uruchomienie, możesz ją uruchomić lub zastąpić nową, korzystając z zasady IstniejącyPeriodicWorkPolicy.

Więcej informacji o tym, jak zaplanować publikację WorkRequest, znajdziesz w dokumentacji WorkManager.

  1. Na początku klasy RefreshDataWorker dodaj obiekt towarzyszący. Zdefiniuj nazwę zadania, aby jednoznacznie zidentyfikować tego pracownika.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. Na zajęciach w DevByteApplication zaplanuj zadanie za pomocą metody enqueueUniquePeriodicWork() na końcu metody setupRecurringWork(). Przekazano wyliczenie KEEP klasy IstniejącePeriodicWorkPolicy. Przekaż repeatingRequest jako parametr PeriodicWorkRequest.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

Jeśli istnieją zadania oczekujące (nieukończone), które mają taką samą nazwę, parametr ExistingPeriodicWorkPolicy.KEEP sprawia, że element WorkManager zachowuje poprzednią aktywność cykliczną i odrzuca nowe zadanie.

  1. Na początku klasy DevByteApplication utwórz obiekt CoroutineScope. Przekaż do elementu Dispatchers.Default jako parametr konstruktora.
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. W klasie DevByteApplication dodaj nową metodę o nazwie delayedInit(), aby rozpocząć współpracę.
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. Wywołaj metodę delayedInit() z wywołaniem setupRecurringWork().
  2. Przenieś inicjowanie drewna 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 na dole okna Android Studio. Filtruj według: RefreshDataWorker.
  2. Uruchom aplikację. WorkManager natychmiast planuje zadanie cykliczne.

    W panelu Logcat znajdź informacje o dzienniku, które pokazują, że żądanie zadania jest zaplanowane, a następnie je uruchomisz.
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

Dziennik WM-WorkerWrapper jest wyświetlany w bibliotece WorkManager, więc nie można go zmienić.

Krok 3. (Opcjonalnie) Zaplanuj WorkWork na minimalny okres

W tym kroku skracasz przedział czasu z 1 do 15 minut. W ten sposób możesz zobaczyć logi okresowego żądania pracy w działaniu.

  1. W klasie DevByteApplication w metodzie setupRecurringWork() skomentuj bieżącą definicję repeatingRequest. Dodaj nowe zadanie służbowe z odstępem powtarzanym co 15 min.
// 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 użyj filtra RefreshDataWorker. Aby wyczyścić poprzednie dzienniki, kliknij ikonę Wyczyść dziennik.
  2. Uruchom aplikację, a WorkManager natychmiast zaplanuje zadanie cykliczne. W panelu Logcat zwróć uwagę na logi – żądanie pracy jest wykonywane co 15 minut. Zaczekaj 15 minut, aby zobaczyć inny zestaw dzienników żądań pracy. Nie zamykaj aplikacji ani nie zamykaj jej. Menedżer pracy powinien nadal działać.

    Pamiętaj, że odstęp między czasem nie przekracza 15 minut, a czasami nawet ponad 15 minut. Dokładny czas wykorzystania zależy od optymalizacji baterii systemu operacyjnego.
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! Instancja robocza została utworzona i zaplanowana do wysłania przez WorkManager. Pojawił się jednak problem: nie określono żadnych ograniczeń. WorkManager zaplanuje pracę raz dziennie, nawet jeśli bateria urządzenia jest słaba, nie śpisz lub nie ma połączenia z siecią. Zmniejszy to wydajność baterii i wydajność urządzenia, co może pogorszyć wygodę użytkowników.

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

W poprzednim zadaniu zaplanowano prośbę o wykonanie zadania: WorkManager. W tym zadaniu dodasz kryteria określające czas wykonania zadania.

Podczas definiowania właściwości WorkRequest możesz określić ograniczenia dotyczące czasu uruchamiania obiektu Worker. Możesz na przykład określić, że działanie ma być uruchamiane tylko wtedy, gdy urządzenie jest bezczynne lub podłączone do zasilania i połączone z Wi-Fi. Możesz też określić zasady działania ponowienia próby. Obsługiwane ograniczenia to metody ustawione w Constraints.Builder. Więcej informacji znajdziesz w artykule Definiowanie zadań służbowych.

Krok 1. Dodaj obiekt ograniczeń i ustaw jedno ograniczenie

W tym kroku utworzysz obiekt Constraints i ustawisz dla niego jedno ograniczenie, czyli typ sieci. Łatwiej zauważyć logi za pomocą tylko jednego ograniczenia. W późniejszym kroku dodaj inne ograniczenia).

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

Aby naprawić ten błąd, zaimportuj androidx.work.Constraints.

  1. Użyj metody setRequiredNetworkType(), aby dodać ograniczenie typu sieci do obiektu constraints. Użyj wyliczenia UNMETERED, aby zadanie zostało uruchomione tylko wtedy, gdy urządzenie jest w sieci bez pomiaru.
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. Aby wygenerować ograniczenia w konstruktorze, użyj metody build().
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

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

  1. W klasie DevByteApplication w metodzie setupRecurringWork() ustaw obiekt Constraints na okresowe żądanie pracy 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 sprawdź dzienniki

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

  1. Odinstaluj aplikację z urządzenia lub emulatora, aby anulować wcześniej zaplanowane zadania.
  2. Otwórz panel Logcat w Android Studio. W panelu Logcat wyczyść poprzednie logi, klikając ikonę Wyczyść dziennik po lewej stronie. Filtruj według: work.
  3. Wyłącz Wi-Fi w urządzeniu lub emulatorze, aby sprawdzić, jak działają ograniczenia. Bieżący kod ustawia tylko jedno ograniczenie, co oznacza, że żądanie powinno być wykonywane tylko w sieci bez pomiaru. Ponieważ Wi-Fi jest wyłączone, urządzenie nie jest połączone z siecią, z pomiarem użycia danych ani bez pomiaru. To ograniczenie nie zostanie spełnione.
  4. Uruchom aplikację i zobacz panel Logcat. WorkManager natychmiast planuje zadanie w tle. Zadanie nie jest spełnione, ponieważ zadanie nie jest uruchomione.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. Włącz Wi-Fi w urządzeniu lub emulatorze i obejrzyj panel Logcat. Teraz zaplanowane zadanie w tle jest uruchamiane mniej więcej co 15 minut, o ile limit sieci jest spełniony.
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 ograniczenia PeriodicWorkRequest następujące ograniczenia:

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

Zaimplementuj to w klasie DevByteApplication.

  1. W klasie DevByteApplication wewnątrz metody setupRecurringWork() określ, że żądanie pracy powinno być uruchamiane tylko wtedy, gdy bateria jest słaba. Dodaj ograniczenie przed wywołaniem metody build() i użyj metody setRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)
  1. Zaktualizuj żądanie, by było ono aktywne tylko podczas ładowania urządzenia. Dodaj ograniczenie przed wywołaniem metody build() i użyj metody setRequiresCharging().
.setRequiresCharging(true)
  1. Zaktualizuj żądanie, aby działało tylko wtedy, gdy urządzenie jest nieaktywne. Dodaj ograniczenie przed wywołaniem metody build() i użyj metody setRequiresDeviceIdle(). To ograniczenie działa wtedy, gdy użytkownik nie korzysta aktywnie z urządzenia. Ta funkcja jest dostępna tylko na urządzeniach z Androidem 6.0 (Marshmallow) lub nowszym, dodaj więc warunek do pakietu SDK w wersji 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 ramach metody setupRecurringWork() zmień interwał żądania na jeden raz dziennie.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

Oto pełna implementacja metody setupRecurringWork() z dziennikiem, która ułatwia śledzenie zaplanowanego czasu wykonywania 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ąć zaplanowane wcześniej żądanie pracy, odinstaluj aplikację DevBytes z urządzenia lub emulatora.
  2. Uruchom aplikację, a WorkManager od razu zaplanuje wysłanie żądania służbowego. Żądanie działa raz dziennie, gdy są 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.

Brawo! Masz zaplanowaną i zaplanowaną prośbę o baterię do codziennego pobierania filmów z wyprzedzeniem w aplikacji DevBytes. WorkManager planuje i uruchamia zadania, optymalizując zasoby systemowe. Twoi użytkownicy i ich baterie będą bardzo zadowoleni.

Projekt na Android Studio: DevBytesWorkManager.

  • Interfejs API WorkManager ułatwia planowanie odroczonych zadań asynchronicznych, które muszą być uruchamiane niezawodnie.
  • Większość rzeczywistych aplikacji wymaga wykonywania długotrwałych zadań w tle. Aby zaplanować takie zadanie w sposób efektywny i wydajny, skorzystaj z WorkManager.
  • Główne klasy biblioteki w WorkManager to Worker, WorkRequest i WorkManager.
  • Klasa Worker reprezentuje jednostkę pracy. Aby wdrożyć zadanie w tle, rozwiń klasę Worker i zastąp metodę doWork().
  • Klasa WorkRequest reprezentuje żądanie wykonania jednostki pracy. WorkRequest to klasa podstawowa do określania parametrów zadań zaplanowanych na WorkManager.
  • Dostępne są 2 konkretne implementacje klasy WorkRequest: OneTimeWorkRequest w przypadku jednorazowych zadań i PeriodicWorkRequest w przypadku okresowych żądań pracy.
  • Podczas definiowania właściwości WorkRequest możesz określić wartość Constraints, która wskazuje, kiedy Worker ma być uruchamiany. Ograniczenia dotyczą między innymi tego, czy urządzenie jest podłączone, nieaktywne czy połączone z Wi-Fi.
  • Aby dodać ograniczenia do obiektu WorkRequest, użyj metod opisanych w dokumentacji Constraints.Builder. Aby na przykład wskazać, że WorkRequest nie ma być uruchamiany, gdy bateria urządzenia jest słaba, użyj metody setRequiresBatteryNotLow().
  • Po zdefiniowaniu zadania WorkRequest przekaż zadanie do systemu Android. Aby to zrobić, zaplanuj zadanie za pomocą jednej z WorkManager metod enqueue.
  • Dokładny czas wykonania funkcji Worker zależy od ograniczeń używanych w WorkRequest i optymalizacji systemu. WorkManager powstał z myślą o tych ograniczeniach, by zapewnić użytkownikom jak najlepsze działanie.

Kurs Udacity:

Dokumentacja dla programistów Androida:

Inne:

Ta sekcja zawiera listę możliwych zadań domowych dla uczniów, którzy pracują w ramach tego ćwiczenia w ramach kursu prowadzonego przez nauczyciela. To nauczyciel może wykonać te czynności:

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

Nauczyciele mogą wykorzystać te sugestie tak długo, jak chcą lub chcą, i mogą przypisać dowolne zadanie domowe.

Jeśli samodzielnie wykonujesz te ćwiczenia z programowania, możesz sprawdzić swoją wiedzę w tych zadaniach domowych.

Pytanie 1

Jakie są konkretne implementacje klasy WorkRequest?

OneTimeWorkPeriodicRequest

OneTimeWorkRequest i PeriodicWorkRequest

OneTimeWorkRequest i RecurringWorkRequest

OneTimeOffWorkRequest i RecurringWorkRequest

Pytanie 2

Z których z poniższych klas korzysta WorkManager, aby zaplanować zadanie działające w tle w interfejsie API 23 lub nowszym?

▢ Tylko JobScheduler

BroadcastReceiver i AlarmManager

AlarmManager i JobScheduler

Scheduler i BroadcastReceiver

Pytanie 3

Za pomocą którego interfejsu API dodajesz ograniczenia do interfejsu WorkRequest?

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

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

Linki do innych ćwiczeń z programowania w tym kursie znajdziesz na stronie docelowej z ćwiczeniami z podstaw Androida Kotlin.