工作管理

一旦您已 定义您的 Worker您的 WorkRequest,最后一步是将您的工作排队。排队工作最简单的方法是调用 WorkManager enqueue() 方法,并将您要运行的 WorkRequest 传递给它。

Kotlin

val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Java

WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

在排队工作时要小心,避免重复。例如,一个应用程序可能尝试每 24 小时将日志上传到后端服务。如果您不小心,您最终可能会多次排队相同的任务,即使该任务只需要运行一次。要实现此目标,您可以将工作调度为 唯一工作

唯一工作

唯一工作是一个强大的概念,它保证您一次只有一个特定名称的工作实例。与 ID 不同,唯一名称是人类可读的,由开发人员指定,而不是由 WorkManager 自动生成。与 标签 不同,唯一名称仅与单个工作实例相关联。

唯一工作可以应用于一次性工作和周期性工作。您可以通过调用以下方法之一来创建唯一的工序,具体取决于您是要调度重复工作还是一次性工作。

这两个方法都接受 3 个参数

  • uniqueWorkName - 一个 String,用于唯一标识工作请求。
  • existingWorkPolicy - 一个 enum,告诉 WorkManager 如果已经存在具有该唯一名称的未完成工作链,该怎么做。有关更多信息,请参阅 冲突解决策略
  • work - 要调度的 WorkRequest

使用唯一工作,我们可以修复前面提到的重复调度问题。

Kotlin

val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

Java

PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

现在,如果代码在 sendLogs 作业已在队列中的情况下运行,现有作业将被保留,不会添加任何新作业。

如果您需要逐步构建一个很长的任务链,唯一工序序列也很有用。例如,一个照片编辑应用程序可能允许用户撤消一系列操作。每个撤消操作可能需要一段时间,但它们必须按正确的顺序执行。在这种情况下,应用程序可以创建一个“撤消”链,并在需要时将每个撤消操作追加到该链。有关更多详细信息,请参阅 链接工作

冲突解决策略

在调度唯一工作时,您必须告诉 WorkManager 在发生冲突时采取什么操作。您可以通过在排队工作时传递一个枚举来实现这一点。

对于一次性工作,您提供一个 ExistingWorkPolicy,它支持 4 种处理冲突的选项。

  • REPLACE 将现有工作替换为新工作。此选项会取消现有工作。
  • KEEP 保留现有工作,忽略新工作。
  • APPEND 将新工作追加到现有工作的末尾。此策略会导致您的新工作 链接 到现有工作,在现有工作完成之后运行。

现有工作成为新工作的先决条件。如果现有工作变为 CANCELLEDFAILED,新工作也会变为 CANCELLEDFAILED。如果您希望新工作无论现有工作的状态如何都运行,请使用 APPEND_OR_REPLACE 而不是。

  • APPEND_OR_REPLACE 的功能类似于 APPEND,不同之处在于它不依赖于先决条件工作的状态。如果现有工作是 CANCELLEDFAILED,新工作仍然会运行。

对于周期性工作,您需要提供一个 ExistingPeriodicWorkPolicy,它支持两种选项,REPLACEKEEP。这些选项的功能与它们的 ExistingWorkPolicy 对应项相同。

观察您的工作

在排队工作之后,您可以通过 WorkManager 查询其 nameid 或与其关联的 tag 来检查其状态。

Kotlin

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

Java

// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>


查询返回一个 ListenableFuture,其中包含一个 WorkInfo 对象,该对象包括工作的 id、其标签、其当前 State,以及通过 Result.success(outputData) 设置的任何输出数据。

每个方法的 LiveData 变体允许您通过注册监听器来 *观察 WorkInfo 的变化*。例如,如果您想在某些工作成功完成时向用户显示一条消息,您可以按如下方式设置它

Kotlin

workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(), 
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}


Java

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

复杂的工作查询

WorkManager 2.4.0 及更高版本支持使用 WorkQuery 对象对排队作业进行复杂查询。WorkQuery 支持通过其标签、状态和唯一工作名称的组合来查询工作。

以下示例展示了如何找到所有具有标签 “syncTag”、处于 FAILEDCANCELLED 状态且具有 “preProcess” 或 “sync” 唯一工作名称的工作。

Kotlin

val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

Java

WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);

WorkQuery 中,每个组件(标签、状态或名称)都与其他组件进行 AND 操作。组件中的每个值都进行 OR 操作。例如:(name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)

WorkQuery 也适用于 LiveData 等效项 getWorkInfosLiveData()

取消和停止工作

如果您不再需要之前排队的作业运行,您可以要求取消它。可以通过工作项的 nameid 或与其关联的 tag 取消工作。

Kotlin

// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")


Java

// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

在内部,WorkManager 检查工作的 State。如果工作已经 完成,则不会发生任何操作。否则,工作的状态将更改为 CANCELLED,并且工作将来不会运行。任何 WorkRequest 作业,这些作业 依赖于此工作,也将被 CANCELLED

目前,RUNNING 工作将收到一个对 ListenableWorker.onStopped() 的调用。覆盖此方法以处理任何潜在的清理操作。有关更多信息,请参阅 停止正在运行的工作者

停止正在运行的工作者

您正在运行的 Worker 可能被 WorkManager 停止,原因有很多。

  • 您明确要求取消它(例如,通过调用 WorkManager.cancelWorkById(UUID))。
  • 唯一工作 的情况下,您明确地排队了一个新的 WorkRequest,其 ExistingWorkPolicyREPLACE。旧的 WorkRequest 立即被视为已取消。
  • 您的工作的约束条件不再满足。
  • 系统指示您的应用程序出于某种原因停止您的工作。如果超过 10 分钟的执行截止时间,则可能会发生这种情况。该工作将在稍后时间重新安排。

在这些情况下,您的工作者会被停止。

您应该协作中止正在进行的任何工作,并释放您的工作者持有的任何资源。例如,您应该在此时关闭对数据库和文件的打开句柄。有两种机制可供您使用来了解您的工作者何时停止。

onStopped() 回调

WorkManager 会在您的工作者停止后立即调用 ListenableWorker.onStopped()。覆盖此方法以关闭您可能持有的任何资源。

isStopped() 属性

您可以调用 ListenableWorker.isStopped() 方法来检查您的工作者是否已被停止。如果您在工作者中执行长时间运行或重复的操作,您应该经常检查此属性,并将其用作尽快停止工作的信号。

注意:WorkManager 会忽略已收到 onStop 信号的工作者设置的 Result,因为该工作者已被视为已停止。