Grundlagen von Android und Kotlin 09.2: WorkManager

Dieses Codelab ist Teil des Kurses „Grundlagen von Android und Kotlin“. Sie können diesen Kurs am besten nutzen, wenn Sie die Codelabs der Reihe nach durcharbeiten. Alle Codelabs des Kurses sind auf der Landingpage für Codelabs zu den Grundlagen von Android und Kotlin aufgeführt.

Einführung

Die meisten Apps in der Praxis müssen lang andauernde Hintergrundaufgaben ausführen. Eine App kann beispielsweise Dateien auf einen Server hochladen, Daten von einem Server synchronisieren und in einer Room-Datenbank speichern, Protokolle an einen Server senden oder rechenintensive Vorgänge für Daten ausführen. Solche Vorgänge sollten im Hintergrund und nicht im UI-Thread (Hauptthread) ausgeführt werden. Hintergrundaufgaben verbrauchen die begrenzten Ressourcen eines Geräts, z. B. RAM und Akku. Wenn dies nicht richtig gehandhabt wird, kann es zu einer schlechten Nutzererfahrung kommen.

In diesem Codelab erfahren Sie, wie Sie mit WorkManager eine Hintergrundaufgabe auf optimierte und effiziente Weise planen. Weitere Informationen zu anderen verfügbaren Lösungen für die Hintergrundverarbeitung in Android finden Sie im Leitfaden zur Hintergrundverarbeitung.

Was Sie bereits wissen sollten

  • Verwendung der Android Architecture Components ViewModel, LiveData und Room.
  • Transformationen für eine LiveData-Klasse durchführen
  • Coroutine erstellen und starten
  • Binding-Adapter in der Datenbindung verwenden
  • So laden Sie Daten aus dem Cache mithilfe eines Repository-Musters.

Lerninhalte

  • So erstellen Sie ein Worker, das eine Arbeitseinheit darstellt.
  • So erstellen Sie eine WorkRequest, um die Ausführung einer Aufgabe anzufordern.
  • Wie Sie Einschränkungen zum WorkRequest hinzufügen, um zu definieren, wie und wann ein Worker ausgeführt werden soll.
  • WorkManager zum Planen von Hintergrundaufgaben verwenden

Aufgaben

  • Erstelle einen Worker, um eine Hintergrundaufgabe auszuführen, mit der die DevBytes-Videoplaylist aus dem Netzwerk vorab abgerufen wird.
  • Planen Sie die regelmäßige Ausführung des Workers.
  • Fügen Sie dem WorkRequest Einschränkungen hinzu.
  • Planen Sie eine regelmäßige WorkRequest, die einmal täglich ausgeführt wird.

In diesem Codelab arbeiten Sie an der DevBytes-App, die Sie in einem vorherigen Codelab entwickelt haben. Wenn Sie diese App nicht haben, können Sie den Startercode für diese Lektion herunterladen.

In der DevBytes App wird eine Liste von DevByte-Videos angezeigt. Das sind kurze Tutorials, die vom Google Android Developer Relations-Team erstellt wurden. In den Videos werden Entwicklerfunktionen und Best Practices für die Android-Entwicklung vorgestellt.

Sie verbessern die Nutzerfreundlichkeit der App, indem Sie die Videos einmal täglich vorab abrufen. So erhalten Nutzer frische Inhalte, sobald sie die App öffnen.

In dieser Aufgabe laden Sie den Starter-Code herunter und sehen ihn sich an.

Schritt 1: Start-App herunterladen und ausführen

Sie können mit der DevBytes-App weiterarbeiten, die Sie im vorherigen Codelab erstellt haben (falls Sie sie haben). Alternativ können Sie die Starter-App herunterladen.

In dieser Aufgabe laden Sie die Starter-App herunter, führen sie aus und sehen sich den Starter-Code an.

  1. Wenn Sie die DevBytes-App noch nicht haben, laden Sie den DevBytes-Startcode für dieses Codelab aus dem DevBytesRepository-Projekt auf GitHub herunter.
  2. Entpacken Sie den Code und öffnen Sie das Projekt in Android Studio.
  3. Verbinden Sie Ihr Testgerät oder Ihren Emulator mit dem Internet, falls es noch nicht verbunden ist. Erstellen Sie die App und führen Sie sie aus. Die App ruft die Liste der DevByte-Videos aus dem Netzwerk ab und zeigt sie an.
  4. Tippe in der App auf ein beliebiges Video, um es in der YouTube App zu öffnen.

Schritt 2: Code ansehen

Die Starter-App enthält viel Code, der im vorherigen Codelab eingeführt wurde. Der Startercode für dieses Codelab enthält Netzwerk-, Benutzeroberflächen-, Offlinecache- und Repository-Module. Sie können sich auf die Planung der Hintergrundaufgabe mit WorkManager konzentrieren.

  1. Maximieren Sie in Android Studio alle Pakete.
  2. Sehen Sie sich das Paket database an. Das Paket enthält die Datenbankentitäten und die lokale Datenbank, die mit Room implementiert wird.
  3. Sehen Sie sich das Paket repository an. Das Paket enthält die Klasse VideosRepository, die die Datenschicht vom Rest der App abstrahiert.
  4. Sehen Sie sich den restlichen Startcode selbst an. Das vorherige Codelab kann Ihnen dabei helfen.

WorkManager ist eine der Android-Architekturkomponenten und Teil von Android Jetpack. WorkManager ist für Hintergrundaufgaben, die aufgeschoben werden können und garantiert ausgeführt werden müssen:

  • Aufschiebbar bedeutet, dass die Arbeit nicht sofort ausgeführt werden muss. Das Senden von Analysedaten an den Server oder das Synchronisieren der Datenbank im Hintergrund kann beispielsweise auf später verschoben werden.
  • Garantierte Ausführung bedeutet, dass die Aufgabe auch dann ausgeführt wird, wenn die App beendet wird oder das Gerät neu startet.

Während WorkManager Hintergrundaufgaben ausführt, werden Kompatibilitätsprobleme und Best Practices für den Akku- und Systemzustand berücksichtigt. WorkManager ist ab API-Level 14 kompatibel. WorkManager wählt je nach API-Level des Geräts eine geeignete Methode zum Planen einer Hintergrundaufgabe aus. Dazu kann JobScheduler (bei API 23 und höher) oder eine Kombination aus AlarmManager und BroadcastReceiver verwendet werden.

Mit WorkManager können Sie auch Kriterien für die Ausführung der Hintergrundaufgabe festlegen. Sie möchten beispielsweise, dass die Aufgabe nur ausgeführt wird, wenn der Akkustatus, der Netzwerkstatus oder der Ladestatus bestimmte Kriterien erfüllt. Wie Sie Einschränkungen festlegen, erfahren Sie später in diesem Codelab.

In diesem Codelab planen Sie einen Task, um die DevBytes-Videoplaylist einmal täglich aus dem Netzwerk vorab abzurufen. Zum Planen dieser Aufgabe verwenden Sie die WorkManager-Bibliothek.

  1. Öffnen Sie die Datei build.gradle (Module:app) und fügen Sie dem Projekt die WorkManager-Abhängigkeit hinzu.

    Wenn Sie die neueste Version der Bibliothek verwenden, sollte die Lösungs-App wie erwartet kompiliert werden. Wenn nicht, versuchen Sie, das Problem zu beheben, oder kehren Sie zur unten gezeigten Bibliotheksversion zurück.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. Synchronisieren Sie Ihr Projekt und prüfen Sie, ob Kompilierungsfehler auftreten.

Bevor Sie dem Projekt Code hinzufügen, sollten Sie sich mit den folgenden Klassen in der WorkManager-Bibliothek vertraut machen:

  • Worker
    In dieser Klasse definieren Sie die eigentliche Arbeit (die Aufgabe), die im Hintergrund ausgeführt werden soll. Sie erweitern diese Klasse und überschreiben die Methode doWork(). In der Methode doWork() wird Code platziert, der im Hintergrund ausgeführt werden soll, z. B. zum Synchronisieren von Daten mit dem Server oder zum Verarbeiten von Bildern. In dieser Aufgabe implementieren Sie die Worker.
  • WorkRequest
    Diese Klasse stellt eine Anfrage zum Ausführen des Workers im Hintergrund dar. Mit WorkRequest können Sie konfigurieren, wie und wann die Worker-Aufgabe ausgeführt werden soll. Dabei können Sie Constraints wie „Gerät angeschlossen“ oder „WLAN-Verbindung“ verwenden. Sie implementieren die WorkRequest in einer späteren Aufgabe.
  • WorkManager
    In dieser Klasse wird Ihr WorkRequest geplant und ausgeführt. WorkManager plant Arbeitsanfragen so, dass die Last auf Systemressourcen verteilt wird und die von Ihnen angegebenen Einschränkungen berücksichtigt werden. Sie implementieren die WorkManager in einer späteren Aufgabe.

Schritt 1: Worker erstellen

In dieser Aufgabe fügen Sie ein Worker hinzu, um die DevBytes-Videoplaylist im Hintergrund vorab abzurufen.

  1. Erstellen Sie im Paket devbyteviewer ein neues Paket mit dem Namen work.
  2. Erstellen Sie im Paket work eine neue Kotlin-Klasse mit dem Namen RefreshDataWorker.
  3. Erweitern Sie die Klasse RefreshDataWorker aus der Klasse CoroutineWorker. Übergeben Sie context und WorkerParameters als Konstruktorparameter.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. Um den Fehler mit der abstrakten Klasse zu beheben, überschreiben Sie die Methode doWork() in der Klasse RefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}

Eine unterbrechbare Funktion ist eine Funktion, die pausiert und später fortgesetzt werden kann. Eine unterbrechbare Funktion kann einen lang andauernden Vorgang ausführen und darauf warten, dass er abgeschlossen wird, ohne den Hauptthread zu blockieren.

Schritt 2: doWork() implementieren

Die Methode doWork() in der Klasse Worker wird in einem Hintergrundthread aufgerufen. Die Methode wird synchron ausgeführt und sollte ein ListenableWorker.Result-Objekt zurückgeben. Das Android-System gibt einem Worker maximal 10 Minuten Zeit, um die Ausführung abzuschließen und ein ListenableWorker.Result-Objekt zurückzugeben. Nach Ablauf dieser Zeit wird die Worker vom System beendet.

Rufen Sie zum Erstellen eines ListenableWorker.Result-Objekts eine der folgenden statischen Methoden auf, um den Abschlussstatus der Hintergrundarbeit anzugeben:

  • Result.success(): Die Arbeit wurde erfolgreich abgeschlossen.
  • Result.failure(): Die Arbeit wurde mit einem permanenten Fehler abgeschlossen.
  • Result.retry(): Bei der Arbeit ist ein vorübergehender Fehler aufgetreten. Sie sollte wiederholt werden.

In dieser Aufgabe implementieren Sie die Methode doWork(), um die DevBytes-Videoplaylist aus dem Netzwerk abzurufen. Sie können die vorhandenen Methoden in der Klasse VideosRepository wiederverwenden, um die Daten aus dem Netzwerk abzurufen.

  1. Erstellen und instanziieren Sie in der Klasse RefreshDataWorker innerhalb von doWork() ein VideosDatabase-Objekt und ein VideosRepository-Objekt.
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. Rufen Sie in der Klasse RefreshDataWorker innerhalb von doWork() über der return-Anweisung die Methode refreshVideos() in einem try-Block auf. Fügen Sie ein Log hinzu, um zu verfolgen, wann der Worker ausgeführt wird.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

Um den Fehler „Nicht aufgelöste Referenz“ zu beheben, importieren Sie retrofit2.HttpException.

  1. Hier finden Sie die vollständige RefreshDataWorker-Klasse:
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()
   }
}

Eine Worker definiert eine Arbeitseinheit und mit dem WorkRequest wird festgelegt, wie und wann die Arbeit ausgeführt werden soll. Es gibt zwei konkrete Implementierungen der Klasse WorkRequest:

  • Die Klasse OneTimeWorkRequest ist für einmalige Aufgaben vorgesehen. Eine einmalige Aufgabe wird nur einmal ausgeführt.
  • Die Klasse PeriodicWorkRequest ist für periodische Aufgaben vorgesehen, die sich in regelmäßigen Abständen wiederholen.

Aufgaben können einmalig oder regelmäßig sein. Wählen Sie die Klasse entsprechend aus. Weitere Informationen zum Planen wiederkehrender Aufgaben finden Sie in der Dokumentation zu wiederkehrenden Aufgaben.

In dieser Aufgabe definieren und planen Sie einen WorkRequest, um den Worker auszuführen, den Sie in der vorherigen Aufgabe erstellt haben.

Schritt 1: Wiederkehrende Aufgaben einrichten

In einer Android-App ist die Klasse Application die Basisklasse, die alle anderen Komponenten wie Aktivitäten und Dienste enthält. Wenn der Prozess für Ihre Anwendung oder Ihr Paket erstellt wird, wird die Application-Klasse (oder eine beliebige Unterklasse von Application) vor allen anderen Klassen instanziiert.

In dieser Beispiel-App ist die Klasse DevByteApplication eine Unterklasse der Klasse Application. Die Klasse DevByteApplication ist ein guter Ort, um die WorkManager zu planen.

  1. Erstellen Sie in der Klasse DevByteApplication eine Methode namens setupRecurringWork(), um die wiederkehrende Hintergrundarbeit einzurichten.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. Erstellen und initialisieren Sie in der Methode setupRecurringWork() mit der Methode PeriodicWorkRequestBuilder() eine Anfrage für periodische Arbeitsvorgänge, die einmal täglich ausgeführt werden soll. Übergeben Sie die Klasse RefreshDataWorker, die Sie in der vorherigen Aufgabe erstellt haben. Geben Sie ein Wiederholungsintervall von 1 mit einer Zeiteinheit von TimeUnit.DAYS an.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

Importieren Sie java.util.concurrent.TimeUnit, um den Fehler zu beheben.

Schritt 2: WorkRequest mit WorkManager planen

Nachdem Sie Ihre WorkRequest definiert haben, können Sie sie mit WorkManager über die Methode enqueueUniquePeriodicWork() planen. Mit dieser Methode können Sie der Warteschlange ein PeriodicWorkRequest mit einem eindeutigen Namen hinzufügen. Es kann jeweils nur ein PeriodicWorkRequest mit einem bestimmten Namen aktiv sein.

Möglicherweise soll beispielsweise nur ein Synchronisierungsvorgang aktiv sein. Wenn ein Synchronisierungsvorgang aussteht, können Sie ihn entweder ausführen lassen oder ihn mit Ihrer neuen Arbeit ersetzen. Verwenden Sie dazu eine ExistingPeriodicWorkPolicy.

Weitere Informationen zum Planen von WorkRequest finden Sie in der WorkManager-Dokumentation.

  1. Fügen Sie in der Klasse RefreshDataWorker am Anfang der Klasse ein Companion-Objekt hinzu. Definieren Sie einen Namen für den Worker, um ihn eindeutig zu identifizieren.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. Planen Sie in der Klasse DevByteApplication am Ende der Methode setupRecurringWork() die Arbeit mit der Methode enqueueUniquePeriodicWork(). Übergeben Sie den KEEP-Enum für die ExistingPeriodicWorkPolicy. Übergeben Sie repeatingRequest als PeriodicWorkRequest-Parameter.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

Wenn ausstehende (nicht abgeschlossene) Aufgaben mit demselben Namen vorhanden sind, sorgt der Parameter ExistingPeriodicWorkPolicy.KEEP dafür, dass die WorkManager die vorherige periodische Aufgabe beibehält und die neue Arbeitsanfrage verwirft.

  1. Erstellen Sie am Anfang der DevByteApplication-Klasse ein CoroutineScope-Objekt. Übergeben Sie Dispatchers.Default als Konstruktorparameter.
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. Fügen Sie in der Klasse DevByteApplication eine neue Methode namens delayedInit() hinzu, um eine Coroutine zu starten.
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. Rufen Sie in der Methode delayedInit() die Methode setupRecurringWork() auf.
  2. Verschieben Sie die Timber-Initialisierung von der Methode onCreate() zur Methode delayedInit().
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. Fügen Sie in der Klasse DevByteApplication am Ende der Methode onCreate() einen Aufruf der Methode delayedInit() hinzu.
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. Öffnen Sie unten im Android Studio-Fenster den Bereich Logcat. Nach RefreshDataWorker filtern.
  2. Führen Sie die App aus. Die WorkManager plant Ihre wiederkehrenden Aufgaben sofort.

    Im Bereich Logcat sehen Sie die Log-Anweisungen, die zeigen, dass die Arbeitsanfrage geplant und dann erfolgreich ausgeführt wird.
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

Das WM-WorkerWrapper-Log wird aus der WorkManager-Bibliothek angezeigt. Sie können diese Logmeldung also nicht ändern.

Schritt 3 (optional): WorkRequest für ein Mindestintervall planen

In diesem Schritt verringern Sie das Zeitintervall von 1 Tag auf 15 Minuten. So können Sie die Logs für eine Anfrage für periodische Arbeiten in Aktion sehen.

  1. Kommentieren Sie in der Klasse DevByteApplication in der Methode setupRecurringWork() die aktuelle Definition von repeatingRequest aus. Fügen Sie eine neue Arbeitsanfrage mit einem periodischen Wiederholungsintervall von 15 Minuten hinzu.
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. Öffnen Sie den Bereich Logcat in Android Studio und filtern Sie nach RefreshDataWorker. Wenn Sie die vorherigen Logs löschen möchten, klicken Sie auf das Symbol Logcat leeren  .
  2. Führen Sie die App aus. Die WorkManager plant Ihre wiederkehrenden Aufgaben sofort. Sehen Sie sich im Bereich Logcat die Logs an. Die Arbeitsanfrage wird alle 15 Minuten ausgeführt. Warten Sie 15 Minuten, um weitere Logs für Arbeitsanfragen zu sehen. Sie können die App geöffnet lassen oder schließen. Der Work Manager sollte trotzdem ausgeführt werden.

    Das Intervall kann manchmal weniger als 15 Minuten und manchmal mehr als 15 Minuten betragen. (Der genaue Zeitpunkt hängt von den Akkuoptimierungen des Betriebssystems ab.)
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
 

Glückwunsch! Sie haben einen Worker erstellt und die Arbeitsanfrage mit WorkManager geplant. Es gibt jedoch ein Problem: Sie haben keine Einschränkungen angegeben. WorkManager plant die Arbeit einmal täglich ein, auch wenn der Akku des Geräts fast leer ist, das Gerät im Ruhemodus ist oder keine Netzwerkverbindung besteht. Dies wirkt sich auf den Akku und die Leistung des Geräts aus und kann zu einer schlechten Nutzererfahrung führen.

In der nächsten Aufgabe beheben Sie dieses Problem, indem Sie Einschränkungen hinzufügen.

In der vorherigen Aufgabe haben Sie WorkManager verwendet, um eine Arbeitsanfrage zu planen. In dieser Aufgabe fügen Sie Kriterien dafür hinzu, wann die Arbeit ausgeführt werden soll.

Beim Definieren von WorkRequest können Sie Einschränkungen für die Ausführung von Worker angeben. Sie können beispielsweise festlegen, dass die Arbeit nur ausgeführt werden soll, wenn das Gerät im Leerlauf ist oder nur, wenn das Gerät an eine Stromquelle angeschlossen und mit einem WLAN verbunden ist. Sie können auch eine Backoff-Richtlinie für das Wiederholen von Aufgaben angeben. Die unterstützten Einschränkungen sind die festgelegten Methoden in Constraints.Builder. Weitere Informationen finden Sie unter Arbeitsanfragen definieren.

Schritt 1: Constraints-Objekt hinzufügen und eine Einschränkung festlegen

In diesem Schritt erstellen Sie ein Constraints-Objekt und legen eine Einschränkung für das Objekt fest, nämlich eine Einschränkung für den Netzwerktyp. Es ist einfacher, die Logs mit nur einer Einschränkung zu sehen. In einem späteren Schritt fügen Sie weitere Einschränkungen hinzu.)

  1. Definieren Sie in der Klasse DevByteApplication am Anfang von setupRecurringWork() eine val vom Typ Constraints. Verwenden Sie die Methode Constraints.Builder().
val constraints = Constraints.Builder()

Importieren Sie androidx.work.Constraints, um den Fehler zu beheben.

  1. Verwenden Sie die Methode setRequiredNetworkType(), um dem constraints-Objekt eine Einschränkung für den Netzwerktyp hinzuzufügen. Verwenden Sie das Enum UNMETERED, damit die Arbeitsanfrage nur ausgeführt wird, wenn das Gerät mit einem Netzwerk verbunden ist, das nicht nach Volumen abgerechnet wird.
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. Verwenden Sie die Methode build(), um die Einschränkungen aus dem Builder zu generieren.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

Jetzt müssen Sie das neu erstellte Constraints-Objekt für die Arbeitsanfrage festlegen.

  1. Legen Sie in der Klasse DevByteApplication in der Methode setupRecurringWork() das Constraints-Objekt auf die regelmäßige Arbeitsanfrage repeatingRequest fest. Fügen Sie die Methode setConstraints() über dem build()-Methodenaufruf ein, um die Einschränkungen festzulegen.
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

Schritt 2: App ausführen und Protokolle ansehen

In diesem Schritt führen Sie die App aus und sehen, dass die eingeschränkte Arbeitsanfrage in regelmäßigen Abständen im Hintergrund ausgeführt wird.

  1. Wenn Sie die App vom Gerät oder Emulator deinstallieren, werden alle zuvor geplanten Aufgaben abgebrochen.
  2. Öffnen Sie den Bereich Logcat in Android Studio. Löschen Sie im Bereich Logcat die vorherigen Logs, indem Sie links auf das Symbol Logcat löschen  klicken. Nach work filtern.
  3. Deaktivieren Sie das WLAN auf dem Gerät oder Emulator, damit Sie sehen können, wie Einschränkungen funktionieren. Im aktuellen Code wird nur eine Einschränkung festgelegt, die angibt, dass die Anfrage nur in einem Netzwerk mit unbegrenztem Datenvolumen ausgeführt werden soll. Da WLAN deaktiviert ist, ist das Gerät nicht mit dem Netzwerk verbunden, unabhängig davon, ob es sich um ein Netzwerk mit oder ohne Datenvolumenbegrenzung handelt. Daher wird diese Einschränkung nicht erfüllt.
  4. Führen Sie die App aus und sehen Sie sich den Bereich Logcat an. Mit WorkManager wird die Hintergrundaufgabe sofort geplant. Da die Netzwerkbeschränkung nicht erfüllt ist, wird die Aufgabe nicht ausgeführt.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. Aktivieren Sie das WLAN auf dem Gerät oder Emulator und beobachten Sie den Bereich Logcat. Die geplante Hintergrundaufgabe wird jetzt ungefähr alle 15 Minuten ausgeführt, sofern die Netzwerkbeschränkung erfüllt ist.
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 [...]

Schritt 3: Weitere Einschränkungen hinzufügen

In diesem Schritt fügen Sie dem PeriodicWorkRequest die folgenden Einschränkungen hinzu:

  • Der Akku ist nicht schwach.
  • Gerät aufladen
  • Gerät im Leerlauf: Nur ab API-Level 23 (Android M) verfügbar.

Implementieren Sie Folgendes in der Klasse DevByteApplication.

  1. Geben Sie in der Klasse DevByteApplication in der Methode setupRecurringWork() an, dass die Arbeitsanfrage nur ausgeführt werden soll, wenn der Akku nicht schwach ist. Fügen Sie die Einschränkung vor dem Aufruf der Methode build() hinzu und verwenden Sie die Methode setRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)
  1. Aktualisieren Sie die Arbeitsanfrage so, dass sie nur ausgeführt wird, wenn das Gerät geladen wird. Fügen Sie die Einschränkung vor dem Aufruf der Methode build() hinzu und verwenden Sie die Methode setRequiresCharging().
.setRequiresCharging(true)
  1. Aktualisieren Sie die Arbeitsanfrage so, dass sie nur ausgeführt wird, wenn das Gerät inaktiv ist. Fügen Sie die Einschränkung vor dem Methodenaufruf build() hinzu und verwenden Sie die Methode setRequiresDeviceIdle(). Bei dieser Einschränkung wird die Arbeitsanfrage nur ausgeführt, wenn der Nutzer das Gerät nicht aktiv verwendet. Diese Funktion ist nur unter Android 6.0 (Marshmallow) und höher verfügbar. Fügen Sie daher eine Bedingung für die SDK-Version M und höher hinzu.
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

Hier finden Sie die vollständige Definition des constraints-Objekts.

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. Ändern Sie in der Methode setupRecurringWork() das Anfrageintervall wieder auf einmal täglich.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

Hier ist die vollständige Implementierung der Methode setupRecurringWork() mit einem Log, damit Sie nachvollziehen können, wann die regelmäßige Arbeitsanfrage geplant ist.

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. Wenn Sie die zuvor geplante Arbeitsanfrage entfernen möchten, deinstallieren Sie die DevBytes App von Ihrem Gerät oder Emulator.
  2. Führen Sie die App aus. Die Arbeitsanfrage wird sofort von WorkManager geplant. Die Arbeitsanfrage wird einmal täglich ausgeführt, wenn alle Einschränkungen erfüllt sind.
  3. Diese Arbeitsanfrage wird im Hintergrund ausgeführt, solange die App installiert ist, auch wenn sie nicht ausgeführt wird. Aus diesem Grund sollten Sie die App vom Smartphone deinstallieren.

Gut gemacht! Sie haben eine akkusparende Arbeitsanfrage für das tägliche Vorabrufen von Videos in der DevBytes App implementiert und geplant. WorkManager plant und führt die Aufgabe aus und optimiert dabei die Systemressourcen. Ihre Nutzer und ihre Akkus werden sich freuen.

Android Studio-Projekt: DevBytesWorkManager.

  • Mit der WorkManager API lassen sich aufschiebbare, asynchrone Aufgaben, die zuverlässig ausgeführt werden müssen, ganz einfach planen.
  • Die meisten Apps in der Praxis müssen lang andauernde Hintergrundaufgaben ausführen. Verwenden Sie WorkManager, um einen Hintergrundtask auf optimierte und effiziente Weise zu planen.
  • Die wichtigsten Klassen in der WorkManager-Bibliothek sind Worker, WorkRequest und WorkManager.
  • Die Klasse Worker stellt eine Arbeitseinheit dar. Wenn Sie die Hintergrundaufgabe implementieren möchten, erweitern Sie die Klasse Worker und überschreiben Sie die Methode doWork().
  • Die Klasse WorkRequest stellt eine Anfrage zum Ausführen einer Arbeitseinheit dar. WorkRequest ist die Basisklasse zum Angeben von Parametern für Aufgaben, die Sie in WorkManager planen.
  • Es gibt zwei konkrete Implementierungen der Klasse WorkRequest: OneTimeWorkRequest für einmalige Aufgaben und PeriodicWorkRequest für periodische Arbeitsanfragen.
  • Wenn Sie die WorkRequest definieren, können Sie Constraints angeben, um festzulegen, wann die Worker ausgeführt werden soll. Zu den Einschränkungen gehört beispielsweise, ob das Gerät an eine Stromquelle angeschlossen ist, ob es sich im Leerlauf befindet oder ob eine WLAN-Verbindung besteht.
  • Wenn Sie dem WorkRequest Einschränkungen hinzufügen möchten, verwenden Sie die in der Constraints.Builder-Dokumentation aufgeführten Set-Methoden. Wenn Sie beispielsweise angeben möchten, dass WorkRequest nicht ausgeführt werden soll, wenn der Akku des Geräts leer ist, verwenden Sie die Methode setRequiresBatteryNotLow().
  • Nachdem Sie die WorkRequest definiert haben, übergeben Sie die Aufgabe an das Android-System. Planen Sie dazu die Aufgabe mit einer der WorkManager-enqueue-Methoden.
  • Der genaue Zeitpunkt der Ausführung von Worker hängt von den Einschränkungen ab, die in WorkRequest verwendet werden, sowie von Systemoptimierungen. WorkManager ist so konzipiert, dass unter diesen Einschränkungen das bestmögliche Verhalten erzielt wird.

Udacity-Kurs:

Android-Entwicklerdokumentation:

Sonstiges:

In diesem Abschnitt werden mögliche Hausaufgaben für Schüler und Studenten aufgeführt, die dieses Codelab im Rahmen eines von einem Kursleiter geleiteten Kurses durcharbeiten. Es liegt in der Verantwortung des Kursleiters, Folgendes zu tun:

  • Weisen Sie bei Bedarf Aufgaben zu.
  • Teilen Sie den Schülern/Studenten mit, wie sie Hausaufgaben abgeben können.
  • Benoten Sie die Hausaufgaben.

Lehrkräfte können diese Vorschläge nach Belieben nutzen und auch andere Hausaufgaben zuweisen, die sie für angemessen halten.

Wenn Sie dieses Codelab selbst durcharbeiten, können Sie mit diesen Hausaufgaben Ihr Wissen testen.

Frage 1

Was sind die konkreten Implementierungen der Klasse WorkRequest?

▢ OneTimeWorkPeriodicRequest

▢ OneTimeWorkRequest und PeriodicWorkRequest

▢ OneTimeWorkRequest und RecurringWorkRequest

▢ OneTimeOffWorkRequest und RecurringWorkRequest

Frage 2

Welche der folgenden Klassen verwendet WorkManager, um die Hintergrundaufgabe auf API 23 und höher zu planen?

▢ Nur JobScheduler

▢ BroadcastReceiver und AlarmManager

▢ AlarmManager und JobScheduler

▢ Scheduler und BroadcastReceiver

Frage 3

Welche API verwenden Sie, um einem WorkRequest Einschränkungen hinzuzufügen?

▢ setConstraints()

▢ addConstraints()

▢ setConstraint()

▢ addConstraintsToWorkRequest()

Nächste Lektion: 10.1 Stile und Designs

Links zu anderen Codelabs in diesem Kurs finden Sie auf der Landingpage für Android Kotlin Fundamentals-Codelabs.