WorkManager 是一个库,用于在 Android 系统中调度和执行可延期的后台工作。我们建议用其取代 Firebase JobDispatcher。以下指南会引导您完成将 Firebase JobDispatcher 实现迁移到 WorkManager 的过程。
Gradle 设置
如需将 WorkManager 库导入到 Android 项目中,请添加 WorkManager 使用入门中列出的依赖项。
从 JobService 到工作器
FirebaseJobDispatcher
使用
JobService
用作入口点,用于定义需要完成的工作。您可以直接使用 JobService
,或使用 SimpleJobService
。
JobService
类似于以下示例:
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?" } }
如果您使用的是 SimpleJobService
,则将会替换 onRunJob()
,这会返回 @JobResult int
类型。
关键区别在于:直接使用 JobService
时,将在主线程上调用 onStartJob()
,并且由应用负责将工作分摊到后台线程。另一方面,如果您使用的是 SimpleJobService
,则由该服务负责在后台线程上执行您的工作。
WorkManager 具有类似的概念。WorkManager 中的基本工作单元是 ListenableWorker
。另外还有其他有用的工作器子类型,如 Worker
、RxWorker
和 CoroutineWorker
(使用 Kotlin 协程时)。
JobService 映射到 ListenableWorker
如果您直接使用 JobService
,则它映射到的工作器为 ListenableWorker
。如果您使用的是 SimpleJobService
,则应使用 Worker
。
我们继续使用上面的示例 (MyJobService
),看看如何将其转换为 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. } }
WorkManager 中的基本工作单元是 ListenableWorker
。与 JobService.onStartJob()
类似,startWork()
在主线程中调用。这里 MyWorker
实现 ListenableWorker
并返回 ListenableFuture
实例,该实例用于异步指示工作完成。您应该在此处选择自己的线程策略。
此处的 ListenableFuture
最终会返回 ListenableWorker.Result
类型,该类型可以是 Result.success()
、Result.success(Data outputData)
、Result.retry()
、Result.failure()
或 Result.failure(Data outputData)
。如需了解详情,请参阅 ListenableWorker.Result
参考页面。
此处调用了 onStopped()
来告知系统 ListenableWorker
需要停止,要么是因为限制条件不再能够满足(例如,因为网络不再可用),要么是因为调用了 WorkManager.cancel…()
方法。此外,如果操作系统出于某种原因决定关闭您的工作,也可能会调用 onStopped()
。
SimpleJobService 映射到工作器
使用 SimpleJobService
时,上述工作器将如下所示:
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. } }
此处 doWork()
会返回 ListenableWorker.Result
的实例,以同步表示工作完成。这类似于将作业安排在后台线程上完成的 SimpleJobService
。
JobBuilder 映射到 WorkRequest
FirebaseJobBuilder 使用 Job.Builder
来表示 Job
元数据。WorkManager 使用 WorkRequest
来填充此角色。
WorkManager 有两种类型的 WorkRequest
:OneTimeWorkRequest
和 PeriodicWorkRequest
。
如果您当前使用的是 Job.Builder.setRecurring(true)
,那么应创建一个新的 PeriodicWorkRequest
。否则,应使用 OneTimeWorkRequest
。
我们来看看使用 FirebaseJobDispatcher
调度复杂 Job
的代码示例:
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);
要使用 WorkManager 达到同一目的,您需要:
- 构建可用作
Worker
输入的输入数据。 - 使用以上输入数据以及上文为
FirebaseJobDispatcher
指定的限制条件来构建WorkRequest
。 - 将
WorkRequest
加入队列。
设置工作器的输入
FirebaseJobDispatcher
使用 Bundle
将输入数据发送到 JobService
。WorkManager 改为使用 Data
。所以,编写的代码变成:
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();
设置工作器的限制条件
FirebaseJobDispatcher
人使用
Job.Builder.setConstaints(...)
对作业设置限制条件WorkManager 改为使用 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(一次性或定期)
如需创建 OneTimeWorkRequest
和 PeriodicWorkRequest
,您应该使用 OneTimeWorkRequest.Builder
和 PeriodicWorkRequest.Builder
。
如需创建类似于上述 Job
的 OneTimeWorkRequest
,您应执行以下操作:
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();
这里的主要区别在于,WorkManager 的作业始终在设备重新启动后自动保留。
如果您要创建 PeriodicWorkRequest
,则需要执行以下操作:
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();
调度工作
现在您已经定义了 Worker
和 WorkRequest
,接下来可以调度工作了。
由 FirebaseJobDispatcher
定义的每个 Job
都有一个用于唯一标识 Job
的 tag
。它还为应用提供了一种方法,通过调用 setReplaceCurrent
,告知调度程序这个 Job
实例是否要替换现有的 Job
副本。
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);
使用 WorkManager 时,您可以使用 enqueueUniqueWork()
和 enqueueUniquePeriodicWork()
API(分别使用 OneTimeWorkRequest
和 PeriodicWorkRequest
)来实现相同的结果。如需了解详情,请参阅 WorkManager.enqueueUniqueWork()
和 WorkManager.enqueueUniquePeriodicWork()
的参考页面。
代码将如下所示:
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);
取消工作
通过 FirebaseJobDispatcher
,您可以使用以下方法取消工作:
Kotlin
dispatcher.cancel("my-unique-tag")
Java
dispatcher.cancel("my-unique-tag");
使用 WorkManager 时,您可以编写以下代码:
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
WorkManager 通常使用 ContentProvider
自行初始化。
如果您需要进一步控制 WorkManager 管理和调度工作的方式,可以自定义 WorkManager 配置和初始化。