1.概念
WorkManager 是一个 API,可供您轻松调度那些即使在退出应用或重启设备后仍应运行的可靠异步任务
。WorkManager API 是一个适合用来替换所有先前的 Android 后台调度 API(包括 FirebaseJobDispatcher、GcmNetworkManager 和 JobScheduler)的组件
1.1 优点
- 1.工作约束 明确定义工作运行的最佳条件
- 2.workmanager 允许使用灵活的调度窗口调度工作,以运行一次或者重复工作。还可以对任务进行标记,方便调度工作及监控或取消工作组。
- 3.重试策略
- 4.工作链
- 5.无缝集成Rxjava 和协程,灵活插入自己的异步
1.2 使用 WorkManager 实现可靠的工作
WorkManager 适用于需要可靠运行的工作。即使用户导航离开屏幕、退出应用或重启设备也不影响工作的执行。例如:
- 向后端服务发送日志或分析数据
- 定期将应用数据与服务器同步
WorkManager 不适用于应用进程结束时安全终止的进程内后台工作,也不适用于需要立即执行的工作。
2.使用工作
扩展 Worker 类并替换 doWork()
public class UploadWorker extends Worker {
public UploadWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
// Do the work here--in this case, upload the images.
uploadImages();
// Indicate whether the work finished successfully with the Result
//Result.success():工作成功完成。
//Result.failure():工作失败。
//Result.retry():工作失败,
return Result.success();
}
}
开启任务
//只运行一次任务
WorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
.build();
//把任务提交给系统
WorkManager .getInstance(myContext)
.enqueue(uploadWorkRequest);
2.1 自定义workRequest
WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);
WorkRequest 对象包含 WorkManager 调度和运行工作所需的所有信息。其中包括运行工作必须满足的约束、调度信息(例如延迟或重复间隔)、重试配置,并且可能包含输入数据
(如果工作需要)。
WorkRequest 本身是抽象基类。该类有两个派生实现,可用于创建 OneTimeWorkRequest 和 PeriodicWorkRequest 请求
。顾名思义,OneTimeWorkRequest 适用于调度非重复性工作,而 PeriodicWorkRequest 则更适合调度以一定间隔重复执行的工作。
2.2 调度一次性工作
WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);
//也可以添加选项,使用建造者模式
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
// Additional configuration
.build();
2.3 调度定期工作
使用 PeriodicWorkRequest 创建定期执行的 WorkRequest 对象
PeriodicWorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
// Constraints
.build();
注意:可以定义的最短重复间隔是 15 分钟(与 JobScheduler API 相同
2.4 更灵活的运行间隔
在每小时的最后 15 分钟
内运行的定期工作
WorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
1, TimeUnit.HOURS,
15, TimeUnit.MINUTES)
.build();
2.5 工作约束
工作约束定义了工作在什么情况能不能执行。
- NetworkType 约束运行工作所需的网络类型。例如 Wi-Fi (UNMETERED)。
- BatteryNotLow 如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
- RequiresCharging 如果设置为 true,那么工作只能在设备充电时运行。
- DeviceIdle 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果您要运行批量操作,否则可能会降低用户设备上正在积极运行的其他应用的性能,建议您使用此约束。
- StorageNotLow 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。
如需配置工作约束条件
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build();
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setConstraints(constraints)
.build();
2.6 延迟工作
将工作设置为在加入队列后至少经过 10 分钟后再运行
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setInitialDelay(10, TimeUnit.MINUTES)
.build();
2.7 工作状态
一次性工作的状态
对于 one-time 工作请求,工作的初始状态为 ENQUEUED。
在 ENQUEUED 状态下,您的工作会在满足其 Constraints 和初始延迟计时要求后立即运行。接下来,该工作会转为 RUNNING 状态,然后可能会根据工作的结果转为 SUCCEEDED、FAILED 状态;或者,如果结果是 retry,它可能会回到 ENQUEUED 状态
SUCCEEDED、FAILED 和 CANCELLED 均表示此工作的终止状态
定期工作的状态
成功和失败状态仅适用于一次性工作和链式工作。定期工作只有一个终止状态 CANCELLED
BLOCKED 状态
此状态适用于一系列已编排的工作,或者说工作链
2.8 管理工作
定义 Worker 和 WorkRequest 后,最后一步是将工作加入队列。将工作加入队列的最简单方法是调用 WorkManager enqueue() 方法,然后传递要运行的 WorkRequest。
WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);
在将工作加入队列时请小心谨慎,以避免重复。例如,应用可能会每 24 小时尝试将其日志上传到后端服务。如果不谨慎,即使作业只需运行一次,您最终也可能会多次将同一作业加入队列。为了实现此目标,您可以将工作调度为唯一工作。
2.9 唯一工作
可确保同一时刻只有一个具有特定名称的工作实例。唯一工作既可用于一次性工作,也可用于定期工作
WorkManager.enqueueUniqueWork()(用于一次性工作)
WorkManager.enqueueUniquePeriodicWork()(用于定期工作)
uniqueWorkName - 用于唯一标识工作请求的 String。
existingWorkPolicy - 此 enum 可告知 WorkManager:如果已有使用该名称且尚未完成的唯一工作链,应执行什么操作。如需了解详情,请参阅冲突解决政策。
work - 要调度的 WorkRequest。
2.10 冲突解决策略
对于一次性工作,您需要提供一个 ExistingWorkPolicy,它支持用于处理冲突的 4 个选项。
- REPLACE:用新工作替换现有工作。此选项将取消现有工作。
- KEEP:保留现有工作,并忽略新工作。
- APPEND:将新工作附加到现有工作的末尾。此政策将导致您的新工作链接到现有工作,在现有工作完成后运行。
- APPEND_OR_REPLACE 函数类似于 APPEND,不过它并不依赖于先决条件工作状态。即使现有工作变为 CANCELLED 或 FAILED 状态,新工作仍会运行
2.11 观察你的工作
// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
该查询会返回 WorkInfo 对象的 ListenableFuture,该值包含工作的 id、其标记、其当前的 State 以及通过 Result.success(outputData) 设置的任何输出数据
//根据工作的完成状态,通知其他逻辑
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo.getState() != null &&
workInfo.getState() == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show();
}
});
2.12 取消和停止工作
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
如果您不再需要运行先前加入队列的工作,则可以要求将其取消。您可以按工作的 name、id 或与其关联的 tag 取消工作。
WorkManager 会在后台检查工作的 State。如果工作已经完成,系统不会执行任何操作。否则,工作的状态会更改为 CANCELLED,之后就不会运行这个工作。任何依赖于此工作的 WorkRequest 作业也将变为 CANCELLED。
2.13 停止正在运行的工作
正在运行的 Worker 可能会由于以下几种原因而停止运行:
- 您明确要求取消它(例如,通过调用 WorkManager.cancelWorkById(UUID) 取消)。
- 如果是唯一工作,您明确地将 ExistingWorkPolicy 为 REPLACE 的新 WorkRequest 加入到了队列中。旧的 WorkRequest 会立即被视为已取消。
- 您的工作约束条件已不再满足。
- 系统出于某种原因指示您的应用停止工作。如果超过 10 分钟的执行期限,可能会发生这种情况。该工作会调度为在稍后重试。
在这些情况下,您的工作器会停止。
2.14 onStopped() 回调
在您的工作器停止后,WorkManager 会立即调用 ListenableWorker.onStopped()。替换此方法可关闭您可能保留的所有资源。
2.15 isStopped() 属性
您可以调用 ListenableWorker.isStopped() 方法以检查工作器是否已停止