Migracja z Firebase JobDispatcher do WorkManager

WorkManager to biblioteka do planowania i wykonywania pracy w tle z możliwością odroczenia na Androidzie. Jest to zalecany zamiennik Firebase JobDispatcher. ten przewodnik poprowadzi Cię przez proces migracji Firebase Implementacja JobDispatcher w WorkManager.

Konfiguracja Gradle

Aby zaimportować bibliotekę WorkManager do projektu na Androida, dodaj do projektu na Androida zależność wymieniona w Wprowadzenie do WorkManagera.

Z JobService do instancji roboczych

FirebaseJobDispatcher używa podklasy JobService jako punkt wyjścia do określenia działań, które należy wykonać. Możesz być korzystasz z aplikacji JobService bezpośrednio lub za pomocą SimpleJobService

JobService będzie wyglądać mniej więcej tak:

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?"
    }
}

Jeśli używasz wersji SimpleJobService, zastąpisz wersję onRunJob(), , która zwraca typ @JobResult int.

Główna różnica polega na tym, że gdy używasz bezpośrednio JobService, onStartJob() jest wywoływane w wątku głównym i to zadaniem aplikacji jest odciążenie z wątkiem w tle. Jeśli natomiast używasz SimpleJobService, ta usługa jest odpowiedzialna za wykonanie Twojej pracy na w wątku w tle.

WorkManager ma podobne koncepcje. Podstawową jednostką pracy w usłudze WorkManager jest: ListenableWorker. Istnieją również inne przydatne podtypy instancji roboczych, Worker RxWorker i CoroutineWorker (gdy za pomocą współprogramów Kotlin).

Usługa JobService jest mapowana na element ListenableWorker

Jeśli używasz bezpośrednio zasobu JobService, oznacza to, że instancja robocza, na którą jest mapowana, jest ListenableWorker Jeśli korzystasz z SimpleJobService, użyj Worker.

Spójrzmy na przykład powyżej (MyJobService) i sprawdźmy, jak możemy go przekonwertować. do: 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.
  }
}

Podstawową jednostką pracy w usłudze WorkManager jest ListenableWorker. Tak jak JobService.onStartJob() funkcja startWork() jest wywoływana w wątku głównym. Tutaj Funkcja MyWorker implementuje funkcję ListenableWorker i zwraca instancję ListenableFuture, , który jest używany do asynchronicznego sygnalizowania zakończenia pracy. Wybierz strategia tworzenia wątków na wątki.

ListenableFuture w tym miejscu zwraca w końcu typ ListenableWorker.Result mogą być wartościami Result.success(), Result.success(Data outputData), Result.retry(), Result.failure() lub Result.failure(Data outputData). Dla: więcej informacji można znaleźć na stronie ListenableWorker.Result

Usługa onStopped() jest wywoływana, aby zasygnalizować, że ListenableWorker musi się zatrzymać. ponieważ ograniczenia nie są już spełnione (na przykład sieć nie jest już dostępna) lub dlatego, że metoda WorkManager.cancel…() została . Pole onStopped() może być też wywoływane, jeśli system operacyjny zdecyduje się wyłączyć z jakiegoś powodu.

Usługa SimpleJobService jest mapowana na instancję roboczą

Przy użyciu funkcji SimpleJobService powyższa instancja robocza będzie wyglądać tak:

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.
  }
}

W tym miejscu funkcja doWork() zwraca wystąpienie ciągu ListenableWorker.Result, aby zasygnalizować działanie synchronicznie. Jest podobny do harmonogramu SimpleJobService, w wątku w tle.

JobBuilder mapuje na WorkRequest

FirebaseJobBuilder używa znaczników Job.Builder do reprezentowania metadanych Job. Menedżer roboczy używa WorkRequest do wypełniania tej roli.

WorkManager ma 2 typy elementów WorkRequest: OneTimeWorkRequest i PeriodicWorkRequest

Jeśli korzystasz obecnie z Job.Builder.setRecurring(true), utwórz nowy element: PeriodicWorkRequest. W przeciwnym razie użyj tagu OneTimeWorkRequest

Zobaczmy, co może zaplanować wykonanie złożonego zadania Job z użytkownikiem FirebaseJobDispatcher wyglądają jak:

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

Aby osiągnąć ten sam efekt za pomocą usługi WorkManager:

  • Utwórz dane wejściowe, które można wykorzystać jako dane wejściowe dla funkcji Worker.
  • Utwórz element WorkRequest z danymi wejściowymi i ograniczeniami podobnymi do tych zdefiniowane powyżej dla zakresu FirebaseJobDispatcher.
  • Umieść WorkRequest w kolejce.

Konfiguruję dane wejściowe instancji roboczej

Funkcja FirebaseJobDispatcher używa Bundle do wysyłania danych wejściowych do funkcji JobService. WorkManager używa zamiast niego Data. No więc który stanie się:

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

Konfigurowanie ograniczeń dla instancji roboczej

Zastosowania: FirebaseJobDispatcher Job.Builder.setConstaints(...). aby skonfigurować ograniczenia zadań. WorkManager używa 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();

Tworzenie żądania WorkRequest (jednorazowe lub okresowe)

Aby utworzyć elementy OneTimeWorkRequest i PeriodicWorkRequest, należy użyć OneTimeWorkRequest.Builder. i PeriodicWorkRequest.Builder.

Aby utworzyć OneTimeWorkRequest, który jest podobny do powyższego Job, musisz wykonaj te czynności:

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

Główna różnica polega na tym, że zadania WorkManagera są zawsze zachowywane gdy urządzenie automatycznie uruchomi się ponownie.

Jeśli chcesz utworzyć element PeriodicWorkRequest, wykonaj następujące czynności:

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

Planowanie pracy

Po zdefiniowaniu pól Worker i WorkRequest możesz przejść do zaplanować pracę.

Każdy element Job określony za pomocą funkcji FirebaseJobDispatcher miał identyfikator tag, który był używany do jednoznacznie identyfikować element Job. Zapewniał też aplikacji dostęp do informacji, algorytmu szeregowania, jeśli ta instancja instancji Job ma zastąpić istniejącą kopię Job, dzwoniąc pod numer 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);

Korzystając z WorkManagera, możesz osiągnąć ten sam efekt przy użyciu Interfejsy API enqueueUniqueWork() i enqueueUniquePeriodicWork() (w przypadku korzystania z OneTimeWorkRequest i PeriodicWorkRequest). Więcej informacje, zapoznaj się ze stronami WorkManager.enqueueUniqueWork() i WorkManager.enqueueUniquePeriodicWork().

Będzie to wyglądać mniej więcej tak:

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

Anuluję zadanie

W usłudze FirebaseJobDispatcher możesz anulować pracę, korzystając z tych usług:

Kotlin

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

Java

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

Korzystając z WorkManagera, możesz:

Kotlin

import androidx.work.WorkManager
WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")

Java

import androidx.work.WorkManager;
WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");

Inicjuję WorkManagera

WorkManager zwykle inicjuje się przy użyciu interfejsu ContentProvider. Jeśli chcesz mieć większą kontrolę nad sposobem organizacji i harmonogramów WorkManagera, może dostosować konfigurację i inicjowanie usługi WorkManager.