Arbeitsanfragen definieren

In der Anleitung „Erste Schritte“ wurde beschrieben, wie Sie eine WorkRequest und in die Warteschlange stellen.

In dieser Anleitung erfahren Sie, wie Sie WorkRequest-Objekte definieren und anpassen, um häufige Anwendungsfälle zu verarbeiten, z. B.:

  • Einmalige und wiederkehrende Arbeit planen
  • Arbeitseinschränkungen festlegen, z. B. WLAN oder Aufladen erforderlich
  • Mindestverzögerung bei der Ausführung von Arbeit garantieren
  • Strategien für Wiederholungen und Backoff festlegen
  • Eingabedaten an die Arbeit übergeben
  • Zusammengehörige Arbeit mit Tags gruppieren

Übersicht

Arbeit wird in WorkManager mit einer WorkRequest definiert. Wenn Sie Arbeit mit WorkManager planen möchten, müssen Sie zuerst ein WorkRequest-Objekt erstellen und es dann in die Warteschlange stellen.

Kotlin

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

Java

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

Das WorkRequest-Objekt enthält alle Informationen, die WorkManager zum Planen und Ausführen Ihrer Arbeit benötigt. Dazu gehören Einschränkungen, die erfüllt sein müssen, damit Ihre Arbeit ausgeführt werden kann, Planungsinformationen wie Verzögerungen oder Wiederholungsintervalle, die Konfiguration für Wiederholungen und möglicherweise Eingabedaten, wenn Ihre Arbeit davon abhängt.

WorkRequest selbst ist eine abstrakte Basisklasse. Es gibt zwei abgeleitete Implementierungen dieser Klasse, mit denen Sie die Anfrage erstellen können: OneTimeWorkRequest und PeriodicWorkRequest. Wie die Namen schon sagen, ist OneTimeWorkRequest nützlich, um nicht wiederkehrende Arbeit zu planen, während PeriodicWorkRequest besser geeignet ist, um Arbeit zu planen, die in einem bestimmten Intervall wiederholt wird.

Einmalige Arbeit planen

Für einfache Arbeit, für die keine zusätzliche Konfiguration erforderlich ist, verwenden Sie die statische Methode from:

Kotlin

val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)

Java

WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);

Für komplexere Arbeit können Sie einen Builder verwenden:

Kotlin

val uploadWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       // Additional configuration
       .build()

Java

WorkRequest uploadWorkRequest =
   new OneTimeWorkRequest.Builder(MyWork.class)
       // Additional configuration
       .build();

Express-Job planen

Mit WorkManager 2.7.0 wurde das Konzept der Express-Jobs eingeführt. So kann WorkManager wichtige Arbeit ausführen und gleichzeitig dem System eine bessere Kontrolle über den Zugriff auf Ressourcen geben.

Express-Jobs zeichnen sich durch die folgenden Merkmale aus:

  • Wichtigkeit: Express-Jobs eignen sich für Aufgaben, die für den Nutzer wichtig sind oder vom Nutzer initiiert werden.
  • Geschwindigkeit: Express-Jobs sind am besten für kurze Aufgaben geeignet, die sofort gestartet und innerhalb weniger Minuten abgeschlossen werden.
  • Kontingente: Ein Kontingent auf Systemebene, das die Ausführungszeit im Vordergrund begrenzt, bestimmt, ob ein Express-Job gestartet werden kann.
  • Energieverwaltung: Einschränkungen bei der Energieverwaltung wie Battery Saver und Doze wirken sich weniger auf Express-Jobs aus.
  • Latenz: Das System führt Express-Jobs sofort aus, sofern die aktuelle Arbeitslast des Systems dies zulässt. Das bedeutet, dass sie latenz empfindlich sind und nicht für eine spätere Ausführung geplant werden können.

Ein möglicher Anwendungsfall für Express-Jobs ist eine Chat-App, wenn der Nutzer eine Nachricht oder ein angehängtes Bild senden möchte. Ebenso kann eine App, die einen Zahlungs- oder Abo-Ablauf verarbeitet, Express-Jobs verwenden. Das liegt daran, dass diese Aufgaben für den Nutzer wichtig sind, schnell im Hintergrund ausgeführt werden, sofort beginnen müssen und auch dann ausgeführt werden sollten, wenn der Nutzer die App schließt.

Kontingente

Das System muss einem Express-Job Ausführungszeit zuweisen, bevor er ausgeführt werden kann. Die Ausführungszeit ist nicht unbegrenzt. Stattdessen erhält jede App ein Kontingent an Ausführungszeit. Wenn Ihre App ihre Ausführungszeit nutzt und das zugewiesene Kontingent erreicht, können Sie keine Express-Jobs mehr ausführen, bis das Kontingent aktualisiert wird. So kann Android Ressourcen effektiver zwischen Anwendungen ausgleichen.

Die einer App zur Verfügung stehende Ausführungszeit basiert auf dem Standby-Bucket und der Prozesswichtigkeit.

Sie können festlegen, was passiert, wenn das Ausführungskontingent die sofortige Ausführung eines Express-Jobs nicht zulässt. Weitere Informationen finden Sie in den folgenden Snippets.

Express-Job ausführen

Ab WorkManager 2.7 kann Ihre App setExpedited() aufrufen, um zu deklarieren, dass eine WorkRequest so schnell wie möglich mit einem Express-Job ausgeführt werden soll. Das folgende Code-Snippet zeigt ein Beispiel für die Verwendung von setExpedited():

Kotlin

val request = OneTimeWorkRequestBuilder<SyncWorker>()
    <b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
    .build()

WorkManager.getInstance(context)
    .enqueue(request)

Java

OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
    .setInputData(inputData)
    <b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
    .build();

In diesem Beispiel initialisieren wir eine Instanz von OneTimeWorkRequest und rufen setExpedited() dafür auf. Diese Anfrage wird dann zu einem Express-Job. Wenn das Kontingent es zulässt, wird sie sofort im Hintergrund ausgeführt. Wenn das Kontingent aufgebraucht ist, gibt der Parameter OutOfQuotaPolicy an, dass die Anfrage als normaler, nicht beschleunigter Job ausgeführt werden soll.

Abwärtskompatibilität und Dienste im Vordergrund

Um die Abwärtskompatibilität für Express-Jobs beizubehalten, führt WorkManager möglicherweise einen Dienst im Vordergrund auf Plattformversionen aus, die älter als Android 12 sind. Dienste im Vordergrund können dem Nutzer eine Benachrichtigung anzeigen.

Mit den Methoden getForegroundInfoAsync() und getForegroundInfo() in Ihrem Worker kann WorkManager eine Benachrichtigung anzeigen, wenn Sie setExpedited() vor Android 12 aufrufen.

Jeder ListenableWorker muss die Methode getForegroundInfo implementieren, wenn Sie anfordern möchten, dass die Aufgabe als Express-Job ausgeführt wird.

Wenn Sie Android 12 oder höher verwenden, stehen Ihnen Dienste im Vordergrund weiterhin über die entsprechende Methode setForeground zur Verfügung.

Worker

Worker wissen nicht, ob die Arbeit, die sie ausführen, beschleunigt wird oder nicht. Auf einigen Android-Versionen können Worker jedoch eine Benachrichtigung anzeigen, wenn eine WorkRequest beschleunigt wurde.

Dazu stellt WorkManager die Methode getForegroundInfoAsync() bereit, die Sie implementieren müssen, damit WorkManager bei Bedarf eine Benachrichtigung anzeigen kann, um einen ForegroundService für Sie zu starten.

CoroutineWorker

Wenn Sie einen CoroutineWorker verwenden, müssen Sie getForegroundInfo() implementieren. Anschließend übergeben Sie ihn in doWork() an setForeground(). Dadurch wird die Benachrichtigung in Android-Versionen vor 12 erstellt.

Dazu ein Beispiel:

  class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):
   CoroutineWorker(appContext, workerParams) {

   override suspend fun getForegroundInfo(): ForegroundInfo {
       return ForegroundInfo(
           NOTIFICATION_ID, createNotification()
       )
   }

   override suspend fun doWork(): Result {
       TODO()
   }

    private fun createNotification() : Notification {
       TODO()
    }

}

Kontingentrichtlinien

Sie können festlegen, was mit Express-Jobs passiert, wenn Ihre App ihr Ausführungskontingent erreicht. Dazu können Sie setExpedited() übergeben:

  • OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, wodurch der Job als normale Arbeitsanfrage ausgeführt wird. Das vorherige Snippet zeigt dies.
  • OutOfQuotaPolicy.DROP_WORK_REQUEST, wodurch die Anfrage abgebrochen wird, wenn nicht genügend Kontingent vorhanden ist.

Verzögerte Express-Jobs

Das System versucht, einen bestimmten Express-Job so schnell wie möglich nach dem Aufruf auszuführen. Wie bei anderen Jobtypen kann das System den Start neuer Express-Jobs jedoch verzögern, z. B. in den folgenden Fällen:

  • Auslastung: Die Systemauslastung ist zu hoch. Das kann passieren, wenn bereits zu viele Jobs ausgeführt werden oder wenn das System nicht genügend Arbeitsspeicher hat.
  • Kontingent: Das Kontingentlimit für Express-Jobs wurde überschritten. Für Express-Jobs wird ein Kontingentsystem verwendet, das auf den App-Standby-Buckets basiert und die maximale Ausführungszeit innerhalb eines rollierenden Zeitfensters begrenzt. Die für Express-Jobs verwendeten Kontingente sind restriktiver als die für andere Arten von Hintergrundjobs.

Wiederkehrende Arbeit planen

Manchmal muss Ihre App bestimmte Arbeit regelmäßig ausführen. Sie können beispielsweise regelmäßig Ihre Daten sichern, neue Inhalte in Ihre App herunterladen oder Logs auf einen Server hochladen.

So erstellen Sie mit PeriodicWorkRequest ein WorkRequest Objekt, das regelmäßig ausgeführt wird:

Kotlin

val saveRequest =
       PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
    // Additional configuration
           .build()

Java

PeriodicWorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
           // Constraints
           .build();

In diesem Beispiel wird die Arbeit in einem Intervall von einer Stunde geplant.

Das Intervall wird als Mindestzeit zwischen Wiederholungen definiert. Der genaue Zeitpunkt, zu dem der Worker ausgeführt wird, hängt von den Einschränkungen ab, die Sie in Ihrem WorkRequest-Objekt verwenden, und von den Optimierungen, die vom System vorgenommen werden.

Flexible Ausführungsintervalle

Wenn die Art Ihrer Arbeit die Ausführungszeit beeinflusst, können Sie Ihre PeriodicWorkRequest so konfigurieren, dass sie innerhalb eines flexiblen Zeitraums in jedem Intervall ausgeführt wird, wie in Abbildung 1 dargestellt.

Sie können ein flexibles Intervall für einen periodischen Job festlegen. Sie definieren ein Wiederholungsintervall und ein flexibeles Intervall, das eine bestimmte Zeitspanne am Ende des Wiederholungsintervalls angibt. WorkManager versucht, Ihren Job irgendwann während des flexiblen Intervalls in jedem Zyklus auszuführen.

Abbildung 1 : Das Diagramm zeigt wiederkehrende Intervalle mit dem flexiblen Zeitraum, in dem die Arbeit ausgeführt werden kann.

Wenn Sie wiederkehrende Arbeit mit einem flexiblen Zeitraum definieren möchten, übergeben Sie beim Erstellen der PeriodicWorkRequest ein flexInterval zusammen mit dem repeatInterval. Der flexible Zeitraum beginnt bei repeatInterval - flexInterval und endet am Ende des Intervalls.

Im folgenden Beispiel kann die wiederkehrende Arbeit in den letzten 15 Minuten jeder Stunde ausgeführt werden.

Kotlin

val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
       1, TimeUnit.HOURS, // repeatInterval (the period cycle)
       15, TimeUnit.MINUTES) // flexInterval
    .build()

Java

WorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
               1, TimeUnit.HOURS,
               15, TimeUnit.MINUTES)
           .build();

Das Wiederholungsintervall muss größer oder gleich PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS und das flexible Intervall muss größer oder gleich PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS` sein.

Auswirkungen von Einschränkungen auf wiederkehrende Arbeit

Sie können Einschränkungen auf wiederkehrende Arbeit anwenden. Sie können beispielsweise Ihrer Arbeitsanfrage eine Einschränkung hinzufügen, sodass die Arbeit nur ausgeführt wird, wenn das Gerät des Nutzers aufgeladen wird. In diesem Fall wird die PeriodicWorkRequest auch dann nicht ausgeführt, wenn das definierte Wiederholungsintervall abgelaufen ist, bis diese Bedingung erfüllt ist. Das kann dazu führen, dass eine bestimmte Ausführung Ihrer Arbeit verzögert oder sogar übersprungen wird, wenn die Bedingungen nicht innerhalb des Ausführungsintervalls erfüllt werden.

Arbeitseinschränkungen

Constraints sorgen dafür, dass die Arbeit verzögert wird, bis die optimalen Bedingungen erfüllt sind. Die folgenden Einschränkungen sind für WorkManager verfügbar:

NetworkType Beschränkt den Netzwerktyp, der für die Ausführung Ihrer Arbeit erforderlich ist. Beispiel: WLAN (UNMETERED).
BatteryNotLow Wenn diese Option auf „true“ gesetzt ist, wird Ihre Arbeit nicht ausgeführt, wenn sich das Gerät im Energiesparmodus befindet.
RequiresCharging Wenn diese Option auf „true“ gesetzt ist, wird Ihre Arbeit nur ausgeführt, wenn das Gerät aufgeladen wird.
DeviceIdle Wenn diese Option auf „true“ gesetzt ist, muss sich das Gerät des Nutzers im Leerlauf befinden, bevor die Arbeit ausgeführt wird. Das kann nützlich sein, um Batchvorgänge auszuführen, die sich andernfalls negativ auf die Leistung anderer Apps auswirken könnten, die aktiv auf dem Gerät des Nutzers ausgeführt werden.
StorageNotLow Wenn diese Option auf „true“ gesetzt ist, wird Ihre Arbeit nicht ausgeführt, wenn der Speicherplatz des Nutzers auf dem Gerät zu gering ist.

Wenn Sie eine Reihe von Einschränkungen erstellen und sie mit einer bestimmten Arbeit verknüpfen möchten, erstellen Sie eine Constraints Instanz mit dem Constraints.Builder() und weisen Sie sie Ihrem WorkRequest.Builder() zu.

Der folgende Code erstellt beispielsweise eine Arbeitsanfrage, die nur ausgeführt wird, wenn das Gerät des Nutzers aufgeladen wird und mit WLAN verbunden ist:

Kotlin

val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()

val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       .setConstraints(constraints)
       .build()

Java

Constraints constraints = new Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresCharging(true)
       .build();

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setConstraints(constraints)
               .build();

Wenn mehrere Einschränkungen angegeben sind, wird Ihre Arbeit nur ausgeführt, wenn alle Einschränkungen erfüllt sind.

Wenn eine Einschränkung nicht mehr erfüllt ist, während Ihre Arbeit ausgeführt wird, beendet WorkManager Ihren Worker. Die Arbeit wird dann wiederholt, wenn alle Einschränkungen erfüllt sind.

Verspätete Arbeit

Wenn Ihre Arbeit keine Einschränkungen hat oder alle Einschränkungen erfüllt sind, wenn Ihre Arbeit in die Warteschlange gestellt wird, kann das System die Arbeit sofort ausführen. Wenn Sie nicht möchten, dass die Arbeit sofort ausgeführt wird, können Sie festlegen, dass die Arbeit erst nach einer anfänglichen Mindestverzögerung gestartet wird.

Hier ein Beispiel, wie Sie festlegen, dass Ihre Arbeit mindestens 10 Minuten nach dem In-die-Warteschlange-Stellen ausgeführt werden soll:

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

Java

WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

Das Beispiel zeigt, wie Sie eine anfängliche Verzögerung für eine OneTimeWorkRequest festlegen. Sie können aber auch eine anfängliche Verzögerung für eine PeriodicWorkRequest festlegen. In diesem Fall wird nur die erste Ausführung Ihrer wiederkehrenden Arbeit verzögert.

Richtlinie für Wiederholungen und Backoff

Wenn WorkManager Ihre Arbeit wiederholen soll, können Sie Result.retry() von Ihrem Worker zurückgeben. Ihre Arbeit wird dann gemäß einer Backoff-Verzögerung und einer Backoff-Richtlinie neu geplant.

  • Die Backoff-Verzögerung gibt die Mindestwartezeit an, bevor Ihre Arbeit nach dem ersten Versuch wiederholt wird. Dieser Wert darf nicht weniger als 10 Sekunden (oder MIN_BACKOFF_MILLIS) betragen.

  • Die Backoff-Richtlinie definiert, wie die Backoff-Verzögerung für nachfolgende Wiederholungsversuche im Laufe der Zeit erhöht werden soll. WorkManager unterstützt zwei Backoff-Richtlinien: LINEAR und EXPONENTIAL.

Jede Arbeitsanfrage hat eine Backoff-Richtlinie und eine Backoff-Verzögerung. Die Standardrichtlinie ist EXPONENTIAL mit einer Verzögerung von 30 Sekunden. Sie können dies jedoch in der Konfiguration Ihrer Arbeitsanfrage überschreiben.

Hier ein Beispiel für die Anpassung der Backoff-Verzögerung und -Richtlinie:

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       WorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

Java

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setBackoffCriteria(
                       BackoffPolicy.LINEAR,
                       WorkRequest.MIN_BACKOFF_MILLIS,
                       TimeUnit.MILLISECONDS)
               .build();

In diesem Beispiel ist die Mindestverzögerung für Backoff auf den zulässigen Mindestwert von 10 Sekunden festgelegt. Da die Richtlinie LINEAR ist, erhöht sich das Wiederholungsintervall mit jedem neuen Versuch um etwa 10 Sekunden. Wenn die erste Ausführung beispielsweise mit Result.retry() endet, wird sie nach 10 Sekunden wiederholt, gefolgt von 20, 30, 40 usw., wenn die Arbeit nach nachfolgenden Versuchen weiterhin Result.retry() zurückgibt. Wenn die Backoff-Richtlinie auf EXPONENTIAL gesetzt wäre, würde die Sequenz der Wiederholungsdauer eher 20, 40 und 80 betragen.

Arbeit taggen

Jede Arbeitsanfrage hat eine eindeutige ID, mit der Sie die Arbeit später identifizieren können , um sie abzubrechen oder ihren Fortschritt zu beobachten.

Wenn Sie eine Gruppe von logisch zusammenhängender Arbeit haben, kann es auch hilfreich sein, diese Arbeitselemente zu taggen. Durch das Taggen können Sie mit einer Gruppe von Arbeitsanfragen zusammenarbeiten.

Beispielsweise bricht WorkManager.cancelAllWorkByTag(String) alle Arbeitsanfragen mit einem bestimmten Tag ab und WorkManager.getWorkInfosByTag(String) gibt eine Liste der WorkInfo-Objekte zurück, mit denen der aktuelle Arbeitsstatus ermittelt werden kann.

Der folgende Code zeigt, wie Sie Ihrer Arbeit ein Tag „cleanup“ hinzufügen können:

Kotlin

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .addTag("cleanup")
   .build()

Java

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
       .addTag("cleanup")
       .build();

Schließlich können einer einzelnen Arbeitsanfrage mehrere Tags hinzugefügt werden. Intern werden diese Tags als eine Reihe von Strings gespeichert. Mit WorkInfo.getTags() können Sie die Reihe der Tags abrufen, die mit der WorkRequest verknüpft sind.

In Ihrer Worker Klasse können Sie die Reihe der Tags mit ListenableWorker.getTags() abrufen.

Eingabedaten zuweisen

Für Ihre Arbeit sind möglicherweise Eingabedaten erforderlich. Für die Arbeit, bei der ein Bild hochgeladen wird, ist beispielsweise der URI des hochzuladenden Bildes als Eingabe erforderlich.

Eingabewerte werden als Schlüssel/Wert-Paare in einem Data Objekt gespeichert und können für die Arbeitsanfrage festgelegt werden. WorkManager liefert die Eingabe Data an Ihre Arbeit, wenn sie ausgeführt wird. Die Worker Klasse kann mit Worker.getInputData() auf die Eingabeargumente zugreifen. Der folgende Code zeigt, wie Sie eine Worker-Instanz erstellen, für die Eingabedaten erforderlich sind, und wie Sie sie in Ihrer Arbeitsanfrage senden.

Kotlin

// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
   : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       val imageUriInput =
           inputData.getString("IMAGE_URI") ?: return Result.failure()

       uploadFile(imageUriInput)
       return Result.success()
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
   .setInputData(workDataOf(
       "IMAGE_URI" to "http://..."
   ))
   .build()

Java

// Define the Worker requiring input
public class UploadWork extends Worker {

   public UploadWork(Context appContext, WorkerParameters workerParams) {
       super(appContext, workerParams);
   }

   @NonNull
   @Override
   public Result doWork() {
       String imageUriInput = getInputData().getString("IMAGE_URI");
       if(imageUriInput == null) {
           return Result.failure();
       }

       uploadFile(imageUriInput);
       return Result.success();
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
      new OneTimeWorkRequest.Builder(UploadWork.class)
           .setInputData(
               new Data.Builder()
                   .putString("IMAGE_URI", "http://...")
                   .build()
           )
           .build();

Ebenso können Sie mit der Klasse Data einen Rückgabewert ausgeben.

Nächste Schritte

Auf der Seite Status und Beobachtung erfahren Sie mehr über Arbeitsstatus und wie Sie den Fortschritt Ihrer Arbeit beobachten können.