WorkManager ist eine Bibliothek zum Planen und Ausführen zurückgestellter Hintergrundarbeiten in Android. Es ist der empfohlene Ersatz für Firebase JobDispatcher. In der folgenden Anleitung erfahren Sie, wie Sie Ihre Firebase JobDispatcher-Implementierung zu WorkManager migrieren.
Gradle-Einrichtung
Fügen Sie die unter Erste Schritte mit WorkManager aufgeführten Abhängigkeiten hinzu, um die WorkManager-Bibliothek in Ihr Android-Projekt zu importieren.
Vom JobService zu den Workern
FirebaseJobDispatcher
verwendet eine abgeleitete Klasse von JobService
als Einstiegspunkt zum Definieren der auszuführenden Arbeit. Möglicherweise verwenden Sie JobService
direkt oder SimpleJobService
.
Ein JobService
sieht in etwa so aus:
Kotlin
import com.firebase.jobdispatcher.JobParameters import com.firebase.jobdispatcher.JobService class MyJobService : JobService() { override fun onStartJob(job: JobParameters): Boolean { // Do some work here return false // Answers the question: "Is there still work going on?" } override fun onStopJob(job: JobParameters): Boolean { return false // Answers the question: "Should this job be retried?" } }
Java
import com.firebase.jobdispatcher.JobParameters; import com.firebase.jobdispatcher.JobService; public class MyJobService extends JobService { @Override public boolean onStartJob(JobParameters job) { // Do some work here return false; // Answers the question: "Is there still work going on?" } @Override public boolean onStopJob(JobParameters job) { return false; // Answers the question: "Should this job be retried?" } }
Wenn Sie SimpleJobService
verwenden, haben Sie onRunJob()
überschrieben, wodurch ein @JobResult int
-Typ zurückgegeben wird.
Der Hauptunterschied besteht darin, dass onStartJob()
im Hauptthread aufgerufen wird und in der Aufgabe der Anwendung liegt, die Arbeit in einen Hintergrundthread zu übertragen, wenn Sie JobService
direkt verwenden. Wenn Sie dagegen SimpleJobService
verwenden, ist dieser Dienst für die Ausführung Ihrer Arbeit in einem Hintergrundthread verantwortlich.
WorkManager hat ähnliche Konzepte. Die grundlegende Arbeitseinheit in WorkManager ist ein ListenableWorker
. Es gibt auch andere nützliche Untertypen von Workern wie Worker
, RxWorker
und CoroutineWorker
(bei Verwendung von Kotlin-Koroutinen).
JobService wird einem ListenableWorker zugeordnet
Wenn Sie JobService
direkt verwenden, ist der Worker, dem er zugeordnet ist, ein ListenableWorker
. Wenn Sie SimpleJobService
verwenden, sollten Sie stattdessen Worker
verwenden.
Sehen wir uns das Beispiel von oben (MyJobService
) an, um es in ein ListenableWorker
-Element umzuwandeln.
Kotlin
import android.content.Context import androidx.work.ListenableWorker import androidx.work.ListenableWorker.Result import androidx.work.WorkerParameters import com.google.common.util.concurrent.ListenableFuture class MyWorker(appContext: Context, params: WorkerParameters) : ListenableWorker(appContext, params) { override fun startWork(): ListenableFuture<ListenableWorker.Result> { // Do your work here. TODO("Return a ListenableFuture<Result>") } override fun onStopped() { // Cleanup because you are being stopped. } }
Java
import android.content.Context; import androidx.work.ListenableWorker; import androidx.work.ListenableWorker.Result; import androidx.work.WorkerParameters; import com.google.common.util.concurrent.ListenableFuture; class MyWorker extends ListenableWorker { public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) { super(appContext, params); } @Override public ListenableFuture<ListenableWorker.Result> startWork() { // Do your work here. Data input = getInputData(); // Return a ListenableFuture<> } @Override public void onStopped() { // Cleanup because you are being stopped. } }
Die grundlegende Arbeitseinheit in WorkManager ist eine ListenableWorker
. Genau wie JobService.onStartJob()
wird startWork()
im Hauptthread aufgerufen. Hier implementiert MyWorker
ListenableWorker
und gibt eine Instanz von ListenableFuture
zurück, die verwendet wird, um den Arbeitsabschluss asynchron zu signalisieren. Sie sollten hier Ihre eigene Threading-Strategie wählen.
ListenableFuture
gibt hier schließlich einen ListenableWorker.Result
-Typ zurück, entweder Result.success()
, Result.success(Data outputData)
, Result.retry()
, Result.failure()
oder Result.failure(Data outputData)
. Weitere Informationen finden Sie auf der Referenzseite für ListenableWorker.Result
.
onStopped()
wird aufgerufen, um zu signalisieren, dass die ListenableWorker
beendet werden muss, weil entweder die Einschränkungen nicht mehr erfüllt werden (z. B. weil das Netzwerk nicht mehr verfügbar ist) oder weil die Methode WorkManager.cancel…()
aufgerufen wurde. onStopped()
kann auch aufgerufen werden, wenn das Betriebssystem Ihre Arbeit aus irgendeinem Grund herunterfährt.
SimpleJobService wird einem Worker zugeordnet.
Bei Verwendung von SimpleJobService
sieht der obige Worker so aus:
Kotlin
import android.content.Context; import androidx.work.Data; import androidx.work.ListenableWorker.Result; import androidx.work.Worker; import androidx.work.WorkerParameters; class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): Result { TODO("Return a Result") } override fun onStopped() { super.onStopped() TODO("Cleanup, because you are being stopped") } }
Java
import android.content.Context; import androidx.work.Data; import androidx.work.ListenableWorker.Result; import androidx.work.Worker; import androidx.work.WorkerParameters; class MyWorker extends Worker { public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) { super(appContext, params); } @Override public Result doWork() { // Do your work here. Data input = getInputData(); // Return a ListenableWorker.Result Data outputData = new Data.Builder() .putString(“Key”, “value”) .build(); return Result.success(outputData); } @Override public void onStopped() { // Cleanup because you are being stopped. } }
Hier gibt doWork()
eine Instanz von ListenableWorker.Result
zurück, um den Arbeitsabschluss synchron zu signalisieren. Dies ähnelt dem SimpleJobService
, mit dem Jobs in einem Hintergrundthread geplant werden.
JobBuilder wird WorkRequest zugeordnet
FirebaseJobBuilder verwendet Job.Builder
, um Job
-Metadaten darzustellen. WorkManager verwendet WorkRequest
, um diese Rolle auszufüllen.
In WorkManager gibt es zwei Arten von WorkRequest
:
OneTimeWorkRequest
und
PeriodicWorkRequest
.
Wenn Sie derzeit Job.Builder.setRecurring(true)
verwenden, sollten Sie eine neue PeriodicWorkRequest
erstellen. Andernfalls sollten Sie OneTimeWorkRequest
verwenden.
So könnte die Planung einer komplexen Job
mit FirebaseJobDispatcher
aussehen:
Kotlin
val input: Bundle = Bundle().apply { putString("some_key", "some_value") } val job = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyService::class.java) // uniquely identifies the job .setTag("my-unique-tag") // one-off job .setRecurring(false) // don't persist past a device reboot .setLifetime(Lifetime.UNTIL_NEXT_BOOT) // start between 0 and 60 seconds from now .setTrigger(Trigger.executionWindow(0, 60)) // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // retry with exponential backoff .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setConstraints( // only run on an unmetered network Constraint.ON_UNMETERED_NETWORK, // // only run when the device is charging Constraint.DEVICE_CHARGING ) .setExtras(input) .build() dispatcher.mustSchedule(job)
Java
Bundle input = new Bundle(); input.putString("some_key", "some_value"); Job myJob = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyJobService.class) // uniquely identifies the job .setTag("my-unique-tag") // one-off job .setRecurring(false) // don't persist past a device reboot .setLifetime(Lifetime.UNTIL_NEXT_BOOT) // start between 0 and 60 seconds from now .setTrigger(Trigger.executionWindow(0, 60)) // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // retry with exponential backoff .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) // constraints that need to be satisfied for the job to run .setConstraints( // only run on an unmetered network Constraint.ON_UNMETERED_NETWORK, // only run when the device is charging Constraint.DEVICE_CHARGING ) .setExtras(input) .build(); dispatcher.mustSchedule(myJob);
Um dies mit WorkManager zu erreichen, müssen Sie Folgendes tun:
- Erstellt Eingabedaten, die als Eingabe für
Worker
verwendet werden können. - Erstellen Sie eine
WorkRequest
mit den Eingabedaten und Einschränkungen, die den oben fürFirebaseJobDispatcher
definierten Werten ähneln. - Die
WorkRequest
in die Warteschlange stellen.
Eingaben für den Worker einrichten
FirebaseJobDispatcher
verwendet eine Bundle
, um Eingabedaten an das JobService
zu senden.
Im WorkManager wird stattdessen Data
verwendet. Daraus ergibt sich Folgendes:
Kotlin
import androidx.work.workDataOf val data = workDataOf("some_key" to "some_val")
Java
import androidx.work.Data; Data input = new Data.Builder() .putString("some_key", "some_value") .build();
Einschränkungen für den Worker einrichten
FirebaseJobDispatcher
verwendet Job.Builder.setConstaints(...)
, um Einschränkungen für Jobs einzurichten. WorkManager verwendet stattdessen Constraints
.
Kotlin
import androidx.work.* val constraints: Constraints = Constraints.Builder().apply { setRequiredNetworkType(NetworkType.CONNECTED) setRequiresCharging(true) }.build()
Java
import androidx.work.Constraints; import androidx.work.Constraints.Builder; import androidx.work.NetworkType; Constraints constraints = new Constraints.Builder() // The Worker needs Network connectivity .setRequiredNetworkType(NetworkType.CONNECTED) // Needs the device to be charging .setRequiresCharging(true) .build();
WorkRequest erstellen (einmalig oder regelmäßig)
Zum Erstellen von OneTimeWorkRequest
s und PeriodicWorkRequest
s sollten Sie OneTimeWorkRequest.Builder
und PeriodicWorkRequest.Builder
verwenden.
So erstellen Sie ein OneTimeWorkRequest
, das dem obigen Job
ähnelt:
Kotlin
import androidx.work.* import java.util.concurrent.TimeUnit val constraints: Constraints = TODO("Define constraints as above") val request: Tell which work to execute OneTimeWorkRequestBuilder<MyWorker>() // Sets the input data for the ListenableWorker .setInputData(input) // If you want to delay the start of work by 60 seconds .setInitialDelay(60, TimeUnit.SECONDS) // Set a backoff criteria to be used when retry-ing .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS) // Set additional constraints .setConstraints(constraints) .build()
Java
import androidx.work.BackoffCriteria; import androidx.work.Constraints; import androidx.work.Constraints.Builder; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest.Builder; import androidx.work.Data; // Define constraints (as above) Constraints constraints = ... OneTimeWorkRequest request = // Tell which work to execute new OneTimeWorkRequest.Builder(MyWorker.class) // Sets the input data for the ListenableWorker .setInputData(inputData) // If you want to delay the start of work by 60 seconds .setInitialDelay(60, TimeUnit.SECONDS) // Set a backoff criteria to be used when retry-ing .setBackoffCriteria(BackoffCriteria.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS) // Set additional constraints .setConstraints(constraints) .build();
Der Hauptunterschied besteht darin, dass die Jobs von WorkManager bei einem Geräteneustart immer automatisch erhalten bleiben.
Wenn Sie eine PeriodicWorkRequest
erstellen möchten, gehen Sie so vor:
Kotlin
val constraints: Constraints = TODO("Define constraints as above") val request: PeriodicWorkRequest = PeriodicWorkRequestBuilder<MyWorker>(15, TimeUnit.MINUTES) // Sets the input data for the ListenableWorker .setInputData(input) // Other setters .build()
Java
import androidx.work.BackoffCriteria; import androidx.work.Constraints; import androidx.work.Constraints.Builder; import androidx.work.NetworkType; import androidx.work.PeriodicWorkRequest; import androidx.work.PeriodicWorkRequest.Builder; import androidx.work.Data; // Define constraints (as above) Constraints constraints = ... PeriodicWorkRequest request = // Executes MyWorker every 15 minutes new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES) // Sets the input data for the ListenableWorker .setInputData(input) . // other setters (as above) .build();
Aufgaben planen
Nachdem Sie eine Worker
und eine WorkRequest
definiert haben, können Sie die Arbeit planen.
Jede mit FirebaseJobDispatcher
definierte Job
hatte eine tag
, mit der ein Job
eindeutig identifiziert wurde. Außerdem bot die Anwendung damit dem Planer die Möglichkeit, durch Aufrufen von setReplaceCurrent
dem Planer mitzuteilen, ob diese Instanz eines Job
eine vorhandene Kopie des Job
ersetzen soll.
Kotlin
val job = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyService::class.java) // uniquely identifies the job .setTag("my-unique-tag") // don't overwrite an existing job with the same tag .setRecurring(false) // Other setters... .build()
Java
Job myJob = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyJobService.class) // uniquely identifies the job .setTag("my-unique-tag") // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // other setters // ... dispatcher.mustSchedule(myJob);
Bei Verwendung von WorkManager können Sie mit den APIs enqueueUniqueWork()
und enqueueUniquePeriodicWork()
dasselbe Ergebnis erzielen (bei Verwendung von OneTimeWorkRequest
bzw. PeriodicWorkRequest
). Weitere Informationen finden Sie auf den Referenzseiten für WorkManager.enqueueUniqueWork()
und WorkManager.enqueueUniquePeriodicWork()
.
Das sieht ungefähr so aus:
Kotlin
import androidx.work.* val request: "A WorkRequest") WorkManager.getInstance(myContext) .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, request)
Java
import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; OneTimeWorkRequest workRequest = // a WorkRequest; WorkManager.getInstance(myContext) // Use ExistingWorkPolicy.REPLACE to cancel and delete any existing pending // (uncompleted) work with the same unique name. Then, insert the newly-specified // work. .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, workRequest);
Aufgabe wird abgebrochen
Mit FirebaseJobDispatcher
können Sie Aufgaben abbrechen mit:
Kotlin
dispatcher.cancel("my-unique-tag")
Java
dispatcher.cancel("my-unique-tag");
Mit WorkManager haben Sie folgende Möglichkeiten:
Kotlin
import androidx.work.WorkManager WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")
Java
import androidx.work.WorkManager; WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");
WorkManager wird initialisiert
WorkManager wird in der Regel mit einem ContentProvider
initialisiert.
Wenn Sie mehr Kontrolle über die Organisation und Planung von WorkManager benötigen, können Sie die Konfiguration und Initialisierung von WorkManager anpassen.