Von Firebase JobDispatcher zu WorkManager migrieren

WorkManager ist eine Bibliothek zum Planen und Ausführen von zurückgestellten Hintergrundarbeiten. in Android. Es wird als Ersatz für Firebase JobDispatcher empfohlen. Die In der folgenden Anleitung erfahren Sie, wie Sie Firebase JobDispatcher-Implementierung in WorkManager.

Gradle einrichten

Um die WorkManager-Bibliothek in Ihr Android-Projekt zu importieren, fügen Sie den Abhängigkeiten in Erste Schritte mit WorkManager

Vom JobService zu Workern

FirebaseJobDispatcher verwendet eine abgeleitete Klasse von JobService als Einstiegspunkt für die Definition der zu erledigenden Arbeit dienen. Sie könnten JobService direkt verwenden 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. was einen @JobResult int-Typ zurückgibt.

Der Hauptunterschied besteht darin, wenn Sie JobService direkt verwenden, onStartJob() wird im Hauptthread aufgerufen, und es liegt in der Verantwortung der App, das mit einem Hintergrund-Thread arbeiten. Wenn Sie jedoch SimpleJobService, ist dieser Dienst für die Ausführung Ihrer Arbeit auf einem im Hintergrund.

WorkManager hat ähnliche Konzepte. Die grundlegende Arbeitseinheit in WorkManager ist ListenableWorker. Es gibt auch andere nützliche Untertypen von Workern, Worker, RxWorker und CoroutineWorker (wenn mit Kotlin-Koroutinen).

JobService wird einem ListenableWorker zugeordnet

Wenn Sie JobService direkt verwenden, ist der Worker, dem sie zugeordnet ist, ein ListenableWorker. Wenn Sie SimpleJobService verwenden, sollten Sie Worker.

Wir verwenden das obige Beispiel (MyJobService) und sehen uns an, wie wir es konvertieren können. zu einem ListenableWorker.

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 Grundeinheit in WorkManager ist ein ListenableWorker. Genau wie JobService.onStartJob(), startWork() wird im Hauptthread aufgerufen. Hier MyWorker implementiert ListenableWorker und gibt eine Instanz von ListenableFuture, Damit wird der Abschluss der Arbeit asynchron signalisiert. Sie sollten Ihre eigene Threading-Strategie.

ListenableFuture gibt hier schließlich einen ListenableWorker.Result-Typ zurück. Result.success(), Result.success(Data outputData), Result.retry(), Result.failure() oder Result.failure(Data outputData). Für Weitere Informationen finden Sie auf der Referenzseite für ListenableWorker.Result

onStopped() wird aufgerufen, um zu signalisieren, dass die ListenableWorker anhalten muss, weil die Einschränkungen nicht mehr erfüllt werden (z. B. weil der nicht mehr verfügbar ist oder weil eine WorkManager.cancel…()-Methode aufgerufen. onStopped() kann auch aufgerufen werden, wenn das Betriebssystem beschließt, funktionieren könnte.

SimpleJobService wird 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 die Arbeit zu signalisieren. synchron abgeschlossen werden. Das ähnelt der Methode SimpleJobService, bei der Jobs in einem Hintergrundthread.

JobBuilder ordnet WorkRequest zu

FirebaseJobBuilder verwendet Job.Builder, um Job-Metadaten darzustellen. WorkManager verwendet WorkRequest, um diese Rolle zu füllen.

In WorkManager gibt es zwei WorkRequest-Typen: OneTimeWorkRequest und PeriodicWorkRequest

Wenn Sie derzeit Job.Builder.setRecurring(true) verwenden, sollten Sie eine neue PeriodicWorkRequest erstellen. Andernfalls sollten Sie OneTimeWorkRequest

Sehen wir uns an, was die Planung einer komplexen Job mit FirebaseJobDispatcher Beispiel:

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 ähneln. oben für FirebaseJobDispatcher definiert.
  • Füge WorkRequest in die Warteschlange ein.

Eingaben für den Worker einrichten

FirebaseJobDispatcher verwendet eine Bundle, um Eingabedaten an JobService zu senden. In WorkManager wird stattdessen Data verwendet. Also 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 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- und PeriodicWorkRequest-Elementen sollten Sie Folgendes verwenden: OneTimeWorkRequest.Builder und PeriodicWorkRequest.Builder.

Um eine OneTimeWorkRequest zu erstellen, die dem obigen Job ähnelt, müssen Sie Gehen Sie so vor:

Kotlin

import androidx.work.*
import java.util.concurrent.TimeUnit

val constraints: Constraints = TODO("Define constraints as above")
val request: OneTimeWorkRequest =
     // 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 wird Ihr Gerät automatisch neu gestartet.

Wenn Sie eine PeriodicWorkRequest erstellen möchten, gehen Sie in etwa 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 nun eine Worker und eine WorkRequest definiert haben, können Sie um die Arbeit zu planen.

Jede mit FirebaseJobDispatcher definierte Job hatte eine tag, die für folgende Aktionen verwendet wurde: ein Job eindeutig identifiziert. Außerdem konnte die Anwendung damit feststellen, den Planer, wenn diese Instanz einer Job eine vorhandene Kopie der Job durch Aufrufen von setReplaceCurrent.

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);

Wenn Sie WorkManager verwenden, enqueueUniqueWork() und enqueueUniquePeriodicWork() API (bei Verwendung einer OneTimeWorkRequest bzw. PeriodicWorkRequest). Weitere Informationen finden Sie auf den Referenzseiten der WorkManager.enqueueUniqueWork() und WorkManager.enqueueUniquePeriodicWork().

Dies sieht ungefähr so aus:

Kotlin

import androidx.work.*

val request: OneTimeWorkRequest = TODO("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 eine Arbeit stornieren mit:

Kotlin

dispatcher.cancel("my-unique-tag")

Java

dispatcher.cancel("my-unique-tag");

Mit WorkManager können Sie Folgendes verwenden:

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 initialisiert sich in der Regel mit einem ContentProvider. Wenn Sie mehr Kontrolle darüber benötigen, wie WorkManager die Arbeit organisiert und plant, können Sie können die WorkManager-Konfiguration und -Initialisierung anpassen.