WorkManager é uma biblioteca usada para a programação e a execução de tarefas adiáveis em segundo plano no Android. Ele é a substituição recomendada para o Firebase JobDispatcher. O guia a seguir traz orientações sobre o processo de migração da implementação do Firebase JobDispatcher para o WorkManager.
Configuração do Gradle
Para importar a biblioteca do WorkManager para seu projeto Android, adicione as dependências listadas em Primeiros passos com o WorkManager.
Do JobService aos workers
FirebaseJobDispatcher
usa uma subclasse de
JobService
como ponto de entrada para definir o trabalho que precisa ser feito. Você pode usar
JobService
diretamente ou
SimpleJobService
(links em inglês).
Um JobService
será parecido com o seguinte:
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 estiver usando SimpleJobService
, você terá substituído onRunJob()
,
que retorna um tipo @JobResult int
.
A principal diferença é que, quando você usar JobService
diretamente, onStartJob()
será chamado na linha de execução principal. Além disso, é responsabilidade do app descarregar o
trabalho em uma linha de execução em segundo plano. Por outro lado, se você estiver usando
SimpleJobService
, esse serviço será responsável por executar seu trabalho em uma linha de execução em
segundo plano.
O WorkManager tem conceitos semelhantes. A unidade de trabalho fundamental no WorkManager é
um ListenableWorker
. Também existem
outros subtipos úteis de workers, como
Worker
,
RxWorker
e CoroutineWorker
(ao
usar corrotinas Kotlin).
Mapeamentos do JobService para um ListenableWorker
Caso você esteja usando JobService
diretamente, o worker para o qual ele será mapeado será um
ListenableWorker
. Caso esteja usando SimpleJobService
, use Worker
.
Vamos ver o exemplo acima (MyJobService
) e analisar como podemos convertê-lo
em um 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. } }
A unidade de trabalho básica no WorkManager é um ListenableWorker
. Assim como
JobService.onStartJob()
, startWork()
é chamado na linha de execução principal. Aqui,
MyWorker
implementa ListenableWorker
e retorna uma instância de
ListenableFuture
,
que é usada para sinalizar a conclusão do trabalho de forma assíncrona. Aqui, você precisa escolher
sua estratégia de linha de execução.
O ListenableFuture
retornará um tipo ListenableWorker.Result
,
que pode ser Result.success()
, Result.success(Data outputData)
,
Result.retry()
, Result.failure()
ou Result.failure(Data outputData)
. Para
mais informações, consulte a página de referência de
ListenableWorker.Result
.
onStopped()
é chamado para sinalizar que o ListenableWorker
precisa ser interrompido
porque as restrições deixaram de ser cumpridas (por exemplo, porque a
rede não está mais disponível) ou porque um método WorkManager.cancel…()
foi
chamado. onStopped()
também poderá ser chamado se o SO encerrar seu
trabalho por algum motivo.
Mapeamentos do SimpleJobService para um Worker
Ao usar SimpleJobService
, o worker acima terá uma aparência semelhante à seguinte:
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. } }
Aqui, doWork()
retorna uma instância de ListenableWorker.Result
para sinalizar a
conclusão de trabalho de forma síncrona. Isso é semelhante ao SimpleJobService
, que programa jobs em uma linha de execução em segundo plano.
Mapeamentos do JobBuilder para WorkRequest
O FirebaseJobBuilder usa Job.Builder
para representar metadados Job
. O WorkManager
usa WorkRequest
para essa função.
O WorkManager tem dois tipos de WorkRequest
s:
OneTimeWorkRequest
e
PeriodicWorkRequest
.
Se você estiver usando Job.Builder.setRecurring(true)
,
crie um novo PeriodicWorkRequest
. Caso contrário, use um
OneTimeWorkRequest
.
Vamos ver como seria a programação de um Job
complexo
com FirebaseJobDispatcher
:
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);
Para conseguir o mesmo resultado com o WorkManager, é preciso:
- criar dados de entrada que possam ser usados como entrada para o
Worker
; - criar um
WorkRequest
com os dados de entrada e restrições semelhantes aos que foram definidos acima paraFirebaseJobDispatcher
; - colocar o
WorkRequest
na fila.
Configurar entradas para o Worker
FirebaseJobDispatcher
usa um Bundle
para enviar dados de entrada para o JobService
.
O WorkManager usa Data
. O
resultado é:
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();
Configurar restrições para o Worker
FirebaseJobDispatcher
usos
Job.Builder.setConstaints(...)
para configurar restrições em jobs. O WorkManager usa
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();
Criar o WorkRequest (único ou periódico)
Para criar OneTimeWorkRequest
s e PeriodicWorkRequest
s, é necessário usar
OneTimeWorkRequest.Builder
e PeriodicWorkRequest.Builder
.
Para criar um OneTimeWorkRequest
semelhante ao Job
acima,
faça o seguinte:
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();
A principal diferença nesse caso é que os jobs do WorkManager são sempre mantidos automaticamente na reinicialização do dispositivo.
Se você quiser criar um PeriodicWorkRequest
, faça algo como o seguinte:
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();
Programar trabalhos
Agora que você definiu um Worker
e uma WorkRequest
, já está pronto para
programar o trabalho.
Cada Job
definido com FirebaseJobDispatcher
tinha uma tag
usada para identificar o
Job
de forma exclusiva. Isso também fornecia uma forma para o aplicativo informar
o programador caso a instância de um Job
precisasse substituir uma cópia existente do
Job
chamando 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);
Com o WorkManager, você pode alcançar o mesmo resultado usando
as APIs enqueueUniqueWork()
e enqueueUniquePeriodicWork()
(ao usar uma
OneTimeWorkRequest
e uma PeriodicWorkRequest
, respectivamente). Para saber mais,
consulte as páginas de referênciareferência de
WorkManager.enqueueUniqueWork()
e WorkManager.enqueueUniquePeriodicWork()
.
O código ficará mais ou menos assim:
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);
Cancelar trabalhos
Você pode cancelar trabalhos com o FirebaseJobDispatcher
usando:
Kotlin
dispatcher.cancel("my-unique-tag")
Java
dispatcher.cancel("my-unique-tag");
Com o WorkManager, você pode usar:
Kotlin
import androidx.work.WorkManager WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")
Java
import androidx.work.WorkManager; WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");
Inicializar o WorkManager
Em geral, o WorkManager é autoinicializado usando um ContentProvider
.
Se você precisa de mais controle sobre como o WorkManager organiza e programa o trabalho,
pode personalizar a configuração e a inicialização dele.