Aufgaben verwalten

Nachdem Sie Ihre Worker und Ihre WorkRequest definiert haben, besteht der letzte Schritt darin, Ihre Arbeit in die Warteschlange zu stellen. Die einfachste Möglichkeit, Aufgaben in die Warteschlange zu stellen, besteht darin, die enqueue()-Methode von WorkManager aufzurufen und die WorkRequest zu übergeben, die ausgeführt werden soll.

Kotlin

val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Java

WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

Seien Sie vorsichtig, wenn Sie Aufgaben in die Warteschlange stellen, um Duplikate zu vermeiden. Eine App kann beispielsweise versuchen, ihre Protokolle alle 24 Stunden in einen Back-End-Dienst hochzuladen. Wenn Sie nicht aufpassen, kann es passieren, dass Sie dieselbe Aufgabe mehrmals in die Warteschlange stellen, obwohl der Job nur einmal ausgeführt werden muss. Um dieses Ziel zu erreichen, können Sie die Arbeit als einmalige Arbeit planen.

Einzigartige Arbeit

Eindeutige Arbeit ist ein leistungsstarkes Konzept, das dafür sorgt, dass Sie jeweils nur eine Instanz von Arbeit mit einem bestimmten Namen haben. Im Gegensatz zu IDs sind eindeutige Namen für Menschen lesbar und werden vom Entwickler angegeben, anstatt von WorkManager automatisch generiert zu werden. Im Gegensatz zu Tags sind eindeutige Namen nur mit einer einzelnen Arbeitsinstanz verknüpft.

Einmalige Arbeit kann sowohl auf einmalige als auch auf wiederkehrende Arbeit angewendet werden. Sie können eine eindeutige Arbeitssequenz erstellen, indem Sie eine dieser Methoden aufrufen. Das hängt davon ab, ob Sie wiederholte oder einmalige Aufgaben planen.

Beide Methoden akzeptieren drei Argumente:

  • uniqueWorkName: Eine String, die zur eindeutigen Identifizierung der Arbeitsanfrage verwendet wird.
  • existingWorkPolicy: Eine enum, die WorkManager anweist, was zu tun ist, wenn bereits eine unfertige Arbeitskette mit diesem eindeutigen Namen vorhanden ist. Weitere Informationen finden Sie in der Richtlinie zur Konfliktlösung.
  • work: die zu planende WorkRequest.

Mit eindeutigen Arbeitsvorgängen können wir das zuvor erwähnte Problem mit der doppelten Terminierung beheben.

Kotlin

val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

Java

PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

Wenn der Code jetzt ausgeführt wird, während sich bereits ein „sendLogs“-Job in der Warteschlange befindet, wird der vorhandene Job beibehalten und kein neuer Job hinzugefügt.

Eindeutige Arbeitsabfolgen können auch nützlich sein, wenn Sie eine lange Kette von Aufgaben schrittweise aufbauen müssen. In einer Bildbearbeitungs-App können Nutzer beispielsweise eine lange Kette von Aktionen rückgängig machen. Jeder dieser Rückgängig-Vorgänge kann eine Weile dauern, muss aber in der richtigen Reihenfolge ausgeführt werden. In diesem Fall könnte die App eine „Rückgängig“-Kette erstellen und bei Bedarf jeden Rückgängig-Vorgang an die Kette anhängen. Weitere Informationen finden Sie unter Arbeit verketten.

Richtlinie zur Konfliktlösung

Wenn Sie eindeutige Arbeitsvorgänge planen, müssen Sie WorkManager mitteilen, welche Aktion bei einem Konflikt ausgeführt werden soll. Dazu übergeben Sie beim Einreihen der Arbeit eine Enumeration.

Für einmalige Aufgaben geben Sie ein ExistingWorkPolicy an, das vier Optionen für den Umgang mit dem Konflikt unterstützt.

  • REPLACE vorhandene Arbeit mit der neuen Arbeit. Mit dieser Option wird die vorhandene Arbeit abgebrochen.
  • KEEP vorhandene Arbeit und ignoriere die neue Arbeit.
  • APPEND die neue Arbeit an das Ende der vorhandenen Arbeit. Durch diese Richtlinie wird Ihr neuer Job verkettet und nach Abschluss des bestehenden Jobs ausgeführt.

Die vorhandene Arbeit wird zur Voraussetzung für die neue Arbeit. Wenn die vorhandene Arbeit CANCELLED oder FAILED wird, ist auch die neue Arbeit CANCELLED oder FAILED. Wenn die neue Aufgabe unabhängig vom Status der vorhandenen Aufgabe ausgeführt werden soll, verwenden Sie stattdessen APPEND_OR_REPLACE.

  • APPEND_OR_REPLACE funktioniert ähnlich wie APPEND, ist aber nicht vom Arbeitsstatus der Voraussetzung abhängig. Wenn die vorhandene Arbeit CANCELLED oder FAILED ist, wird die neue Arbeit trotzdem ausgeführt.

Für die Arbeit im Zeitraum geben Sie ein ExistingPeriodicWorkPolicy an, das die beiden Optionen REPLACE und KEEP unterstützt. Diese Optionen funktionieren genauso wie die entsprechenden Optionen für die Arbeitsrichtlinie.

Ihre Arbeit beobachten

Nachdem Sie Arbeit in die Warteschlange gestellt haben, können Sie ihren Status jederzeit abrufen, indem Sie WorkManager anhand der name, id oder einer zugehörigen tag abfragen.

Kotlin

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

Java

// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>

Die Abfrage gibt ein ListenableFuture eines WorkInfo-Objekts zurück, das die id des Werks, seine Tags, seinen aktuellen State und alle Ausgabedatasets mit Result.success(outputData) enthält.

Mit den Varianten LiveData und Flow der einzelnen Methoden können Sie Änderungen an der WorkInfo beobachten, indem Sie einen Listener registrieren. Wenn Sie dem Nutzer beispielsweise eine Nachricht anzeigen möchten, wenn eine Aufgabe erfolgreich abgeschlossen wurde, können Sie das so einrichten:

Kotlin

workManager.getWorkInfoByIdFlow(syncWorker.id)
          .collect{ workInfo ->
              if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
                  Snackbar.make(requireView(),
                      R.string.work_completed, Snackbar.LENGTH_SHORT)
                      .show()
              }
          }

Java

workManager.getWorkInfoByIdLiveData(syncWorker.id)
        .observe(getViewLifecycleOwner(), workInfo -> {
    if (workInfo.getState() != null &&
            workInfo.getState() == WorkInfo.State.SUCCEEDED) {
        Snackbar.make(requireView(),
                    R.string.work_completed, Snackbar.LENGTH_SHORT)
                .show();
   }
});

Komplexe Arbeitsanfragen

WorkManager 2.4.0 und höher unterstützt komplexe Abfragen für in die Warteschlange eingereihte Jobs mit WorkQuery-Objekten. Mit WorkQuery können Sie nach Arbeitsvorgängen suchen, indem Sie eine Kombination aus Tags, Status und eindeutigem Namen des Arbeitsvorgangs angeben.

Das folgende Beispiel zeigt, wie Sie alle Arbeiten mit dem Tag syncTag finden, die den Status FAILED oder CANCELLED haben und deren Arbeitsname entweder „preProcess“ oder „sync“ ist.

Kotlin

val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

Java

WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);

Jede Komponente (Tag, Status oder Name) in einem WorkQuery wird mit den anderen AND-ed. Jeder Wert in einer Komponente wird OR. Beispiel: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).

WorkQuery funktioniert auch mit dem LiveData-Äquivalent getWorkInfosLiveData() und dem Flow-Äquivalent getWorkInfosFlow().

Arbeit abbrechen und beenden

Wenn Sie nicht mehr möchten, dass die zuvor in die Warteschlange gestellten Aufgaben ausgeführt werden, können Sie sie abbrechen. Arbeiten können von ihrem name, id oder einem zugehörigen tag abgebrochen werden.

Kotlin

// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

Java

// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

Im Hintergrund prüft WorkManager die State der Work. Wenn die Arbeit bereits abgeschlossen ist, passiert nichts. Andernfalls wird der Status der Arbeit zu CANCELLED geändert und die Arbeit wird in Zukunft nicht mehr ausgeführt. Alle WorkRequest-Jobs, die von dieser Arbeit abhängig sind, werden ebenfalls CANCELLED.

RUNNING (Arbeit) erhält einen Anruf an ListenableWorker.onStopped(). Überschreiben Sie diese Methode, um eine mögliche Bereinigung durchzuführen. Weitere Informationen finden Sie unter Aktiven Worker beenden.

Laufenden Worker beenden

Es gibt mehrere Gründe, warum dein Worker möglicherweise von WorkManager beendet wird:

  • Sie haben explizit darum gebeten, dass die Bestellung storniert wird (z. B. durch Aufrufen von WorkManager.cancelWorkById(UUID)).
  • Bei einzigartiger Arbeit haben Sie explizit einen neuen WorkRequest mit einem ExistingWorkPolicy von REPLACE in die Warteschlange gestellt. Das alte WorkRequest wird sofort als gekündigt betrachtet.
  • Die Einschränkungen für Ihre Arbeit werden nicht mehr erfüllt.
  • Das System hat Ihre App aus irgendeinem Grund angewiesen, die Arbeit zu beenden. Das kann passieren, wenn Sie die Ausführungsfrist von 10 Minuten überschreiten. Die Arbeit wird zu einem späteren Zeitpunkt wiederholt.

Unter diesen Bedingungen wird Ihr Worker beendet.

Sie sollten alle laufenden Arbeiten abbrechen und alle Ressourcen freigeben, die Ihr Worker belegt. Schließen Sie beispielsweise offene Handles für Datenbanken und Dateien. Es gibt zwei Mechanismen, mit denen Sie herausfinden können, wann Ihr Worker beendet wird.

onStopped()-Callback

WorkManager ruft ListenableWorker.onStopped() auf, sobald Ihr Worker beendet wurde. Überschreiben Sie diese Methode, um alle Ressourcen zu schließen, die Sie möglicherweise verwenden.

isStopped()-Property

Sie können die Methode ListenableWorker.isStopped() aufrufen, um zu prüfen, ob Ihr Worker bereits beendet wurde. Wenn Sie in Ihrem Worker lang andauernde oder sich wiederholende Vorgänge ausführen, sollten Sie diese Eigenschaft regelmäßig prüfen und sie als Signal verwenden, um die Arbeit so schnell wie möglich zu beenden.

Hinweis:WorkManager ignoriert die von einem Worker festgelegte Result, der das onStop-Signal erhalten hat, da der Worker bereits als beendet gilt.

Status des Stoppgrunds beobachten

Wenn Sie herausfinden möchten, warum ein Worker beendet wurde, können Sie den Grund für das Beenden protokollieren, indem Sie WorkInfo.getStopReason() aufrufen:

Kotlin

workManager.getWorkInfoByIdFlow(syncWorker.id)
  .collect { workInfo ->
      if (workInfo != null) {
        val stopReason = workInfo.stopReason
        logStopReason(syncWorker.id, stopReason)
      }
  }

Java

  workManager.getWorkInfoByIdLiveData(syncWorker.id)
    .observe(getViewLifecycleOwner(), workInfo -> {
        if (workInfo != null) {
          int stopReason = workInfo.getStopReason();
          logStopReason(syncWorker.id, workInfo.getStopReason());
        }
  });