WorkManager è una libreria per la pianificazione e l'esecuzione di lavori in background differibili su Android. È la sostituzione consigliata per Firebase JobDispatcher. La questa guida ti aiuterà a eseguire la migrazione di Firebase Implementazione di JobDispatcher in WorkManager.
Configurazione di Gradle
Per importare la libreria WorkManager nel tuo progetto Android, aggiungi l'elemento elencate in Guida introduttiva all'utilizzo di WorkManager.
Da JobService ai worker
FirebaseJobDispatcher
utilizza una sottoclasse
JobService
come punto di partenza per definire
il lavoro da svolgere. Potresti essere
usando direttamente JobService
SimpleJobService
Un JobService
avrà un aspetto simile a questo:
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?" } }
Se utilizzi SimpleJobService
avrai eseguito l'override di onRunJob()
,
che restituisce un tipo @JobResult int
.
La differenza principale è che usi direttamente JobService
, onStartJob()
viene chiamato nel thread principale ed è responsabilità dell'app scaricare
a un thread in background. D'altra parte, se utilizzi
SimpleJobService
, quel servizio è responsabile dell'esecuzione del tuo lavoro su una
in background.
WorkManager presenta concetti simili. L'unità fondamentale di lavoro in WorkManager è
Un ListenableWorker
. Esistono
ma anche altri utili sottotipi di worker
Worker
,
RxWorker
e CoroutineWorker
(quando
utilizzando le coroutine Kotlin).
JobService viene mappato a un ListenableWorker
Se utilizzi direttamente JobService
, il worker a cui viene mappato è un
ListenableWorker
. Se usi SimpleJobService
, dovresti usare
Worker
in alternativa.
Usiamo l'esempio precedente (MyJobService
) e vediamo come convertirlo
a 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. } }
L'unità di lavoro di base in WorkManager è ListenableWorker
. Proprio come
JobService.onStartJob()
, startWork()
viene chiamato nel thread principale. Qui
MyWorker
implementa ListenableWorker
e restituisce un'istanza di
ListenableFuture
,
che viene utilizzato per segnalare il completamento del lavoro in modo asincrono. Dovresti scegliere il
una strategia di thread.
ListenableFuture
in questo caso restituisce un tipo ListenableWorker.Result
che può essere uno tra Result.success()
, Result.success(Data outputData)
,
Result.retry()
, Result.failure()
o Result.failure(Data outputData)
. Per
ulteriori informazioni, consulta la pagina di riferimento per
ListenableWorker.Result
onStopped()
viene chiamato per segnalare che ListenableWorker
deve essere interrotto,
perché i vincoli non vengono più soddisfatti (ad esempio, perché
non è più disponibile) oppure perché è stato eseguito un metodo WorkManager.cancel…()
chiamato. Potrebbe essere chiamato anche onStopped()
se il sistema operativo decide di arrestare
per qualche motivo.
SimpleJobService viene mappato a un worker
Quando utilizzi SimpleJobService
, il worker riportato sopra avrà il seguente aspetto:
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. } }
In questo caso doWork()
restituisce un'istanza di ListenableWorker.Result
per segnalare il funzionamento
il completamento in modo sincrono. È simile a SimpleJobService
, che prevede
job su un thread in background.
JobBuilder mappa a WorkRequest
FirebaseJobBuilder utilizza Job.Builder
per rappresentare i metadati Job
. Responsabile del lavoro
utilizza WorkRequest
per ricoprire questo ruolo.
WorkManager ha due tipi di WorkRequest
:
OneTimeWorkRequest
e
PeriodicWorkRequest
.
Se al momento utilizzi Job.Builder.setRecurring(true)
, dovresti
crea un nuovo PeriodicWorkRequest
. In caso contrario, devi utilizzare
OneTimeWorkRequest
.
Vediamo cosa potrebbe fare la pianificazione di un Job
complesso con FirebaseJobDispatcher
simile al seguente:
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);
Per ottenere lo stesso risultato con WorkManager, dovrai:
- Crea dati di input che possono essere utilizzati come input per
Worker
. - Crea un
WorkRequest
con i dati di input e i vincoli simili a quelli definita sopra perFirebaseJobDispatcher
. - Metti in coda
WorkRequest
.
Configurazione degli input per il worker
FirebaseJobDispatcher
utilizza un Bundle
per inviare i dati di input a JobService
.
WorkManager utilizza invece Data
. Quindi...
che diventa:
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();
Configurazione dei vincoli per il worker
FirebaseJobDispatcher
usi
Job.Builder.setConstaints(...)
per configurare i vincoli sui job. WorkManager utilizza
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();
Creazione della WorkRequest (una tantum o periodica)
Per creare OneTimeWorkRequest
e PeriodicWorkRequest
, devi usare
OneTimeWorkRequest.Builder
:
e PeriodicWorkRequest.Builder
.
Per creare un OneTimeWorkRequest
che è simile a quanto sopra Job
devi
segui questi passaggi:
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();
La differenza principale è che i job di WorkManager sono sempre resi persistenti il dispositivo si riavvia automaticamente.
Se vuoi creare un PeriodicWorkRequest
, ad esempio:
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();
Programmazione del lavoro
Ora che hai definito un Worker
e un WorkRequest
, è tutto pronto per
pianificare il lavoro.
Ogni Job
definito con FirebaseJobDispatcher
aveva un tag
utilizzato per
identificare in modo univoco un Job
. Ha anche fornito all'applicazione un modo per capire
scheduler se questa istanza di un Job
dovesse sostituire una copia esistente del
Job
chiamando il numero 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);
Quando utilizzi WorkManager, puoi ottenere lo stesso risultato utilizzando
enqueueUniqueWork()
e enqueueUniquePeriodicWork()
API (quando si utilizza un
OneTimeWorkRequest
e PeriodicWorkRequest
, rispettivamente). Per ulteriori informazioni
informazioni, consulta le pagine di riferimento per
WorkManager.enqueueUniqueWork()
e WorkManager.enqueueUniquePeriodicWork()
.
L'URL avrà il seguente aspetto:
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);
Annullamento del lavoro
Con FirebaseJobDispatcher
puoi annullare il lavoro utilizzando:
Kotlin
dispatcher.cancel("my-unique-tag")
Java
dispatcher.cancel("my-unique-tag");
Quando utilizzi WorkManager puoi utilizzare:
Kotlin
import androidx.work.WorkManager WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")
Java
import androidx.work.WorkManager; WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");
Inizializzazione di WorkManager
WorkManager si inizializza in genere utilizzando un ContentProvider
.
Se hai bisogno di un maggiore controllo sul funzionamento dell'organizzazione e delle pianificazioni di WorkManager,
puoi personalizzare la configurazione e l'inizializzazione di WorkManager.