更新已加入队列的工作

WorkManager 允许您在将 WorkRequest 加入队列后对其进行更新。对于频繁更改限制条件或需要实时更新 worker 的大型应用,通常需要这样做。从 WorkManager 版本 2.8.0 开始,updateWork() API 就是实现这一点的方法。

借助 updateWork() 方法,您可以即时更改 WorkRequest 的某些方面,而无需执行手动取消新 Fragment 并将其加入队列的过程。这大大简化了开发过程。

避免取消工作

通常,您应避免取消现有 WorkRequest 并将新的 WorkRequest 加入队列。这样做可能会导致应用重复某些任务,并且可能需要您编写大量额外的代码。

请考虑以下示例,了解取消 WorkRequest 可能导致问题的原因:

  • 后端请求:如果您在 Worker 计算要发送到服务器的载荷时取消它,新的 Worker 需要重新开始并重新计算费用可能较高的载荷。
  • 调度:如果您取消 PeriodicWorkRequest 并希望新的 PeriodicWorkRequest 按相同的时间表执行,则需要计算时间偏移,以确保新的执行时间与上一个工作请求保持一致。

借助 updateWork() API,您可以更新工作请求的约束条件和其他参数,而无需取消新请求并将其加入队列。

何时取消工作

在某些情况下,您应直接取消 WorkRequest,而不是调用 updateWork()。如果您想更改已加入队列的工作的基本性质,就应该这样做。

何时更新单位地址

假设有一款照片应用,它每天都会对用户照片进行备份。为此,它已将 PeriodicWorkRequest 加入队列。WorkRequest 具有约束条件,要求设备需要充电并连接到 WLAN。

不过,用户每天仅使用快速充电器为设备充电 20 分钟。在这种情况下,应用可能需要更新 WorkRequest 以放宽充电约束条件,这样一来,即使设备未充满电,应用也可以上传照片。

在这种情况下,您可以使用 updateWork() 方法来更新工作请求的约束条件。

如何更新单位地址

updateWork() 方法提供了一种更新现有的 WorkRequest 的简单方法,而不必取消新旧 Activity 并将其加入队列。

如需使用“更新已加入队列的工作”,请按以下步骤操作:

  1. 获取已加入队列的工作的现有 ID:获取要更新的 WorkRequest 的 ID。如需检索此 ID,您可以使用任何 getWorkInfo API,也可以手动保留初始 WorkRequest 中的 ID 以供日后使用公共属性 WorkRequest.id 检索,然后再将其加入队列。
  2. 创建新的 WorkRequest:创建一个新的 WorkRequest,并使用 WorkRequest.Builder.setID() 将其 ID 设置为与现有 WorkRequest 的 ID 匹配。
  3. 设置约束条件:使用 WorkRequest.Builder.setConstraints() 传递 WorkManager 新的约束条件。
  4. 调用 updateWork:将新的 WorkRequest 传递给 updateWork()

更新工作示例

以下是 Kotlin 中的示例代码段,演示了如何使用 updateWork() 方法更改用于上传照片的 WorkRequest 的电池电量限制:

suspend fun updatePhotoUploadWork() {
    // Get instance of WorkManager.
    val workManager = WorkManager.getInstance(context)

    // Retrieve the work request ID. In this example, the work being updated is unique
    // work so we can retrieve the ID using the unique work name.
    val photoUploadWorkInfoList = workManager.getWorkInfosForUniqueWork(
        PHOTO_UPLOAD_WORK_NAME
    ).await()

    val existingWorkRequestId = photoUploadWorkInfoList.firstOrNull()?.id ?: return

    // Update the constraints of the WorkRequest to not require a charging device.
    val newConstraints = Constraints.Builder()
        // Add other constraints as required here.
        .setRequiresCharging(false)
        .build()

    // Create new WorkRequest from existing Worker, new constraints, and the id of the old WorkRequest.
    val updatedWorkRequest: WorkRequest =
        OneTimeWorkRequestBuilder<MyWorker>()
            .setConstraints(newConstraints)
            .setId(existingWorkRequestId)
            .build()

    // Pass the new WorkRequest to updateWork().
    workManager.updateWork(updatedWorkRequest)
}

处理结果

updateWork() 会返回 ListenableFuture<UpdateResult>。给定的 UpdateResult 可以是多个值中的一个,用于说明 WorkManager 能否应用您的更改。它还会指明能够应用更改的时间。

如需了解详情,请参阅 updateWork() UpdateResult 参考文档

世代跟踪工作

每次更新 WorkRequest 时,其 generation 都会增加 1。这样,您就可以准确跟踪当前已加入队列的 WorkRequest。在观察、跟踪和测试工作请求时,生成操作可以让您更好地控制。

如需获取 WorkRequest 的生成,请按以下步骤操作:

  1. WorkInfo:调用 WorkManager.getWorkInfoById() 以检索与 WorkRequest 对应的 WorkInfo 实例。
  2. getGeneration:对 WorkInfo 实例调用 getGeneration()。返回的 Int 对应于 WorkRequest 的世代。
    • 请注意,这里没有生成字段或属性,只有 WorkInfo.getGeneration() 方法。

轨道生成示例

以下是上述工作流的实现示例,该工作流用于检索 WorkRequest 的生成。

// Get instance of WorkManager.
val workManager = WorkManager.getInstance(context)

// Retrieve WorkInfo instance.
val workInfo = workManager.getWorkInfoById(oldWorkRequestId)

// Call getGeneration to retrieve the generation.
val generation = workInfo.getGeneration()

工作更新政策

以前,推荐使用 ExistingPeriodicWorkPolicy.REPLACE 政策将 PeriodicWorkRequest 加入队列来更新定期工作。如果存在具有相同唯一 id 的待处理 PeriodicWorkRequest,则新的工作请求将取消并删除它。此政策现已弃用,取而代之的是使用 ExistingPeriodicWorkPolicy.UPDATE 的工作流。

例如,将 enqueueUniquePeriodicWorkPeriodicWorkRequest 搭配使用时,您可以使用 ExistingPeriodicWorkPolicy.UPDATE 政策初始化新的 PeriodicWorkRequest。如果存在具有相同唯一名称的待处理 PeriodicWorkRequest,WorkManager 会将其更新为新规范。按照此工作流操作后,无需使用 updateWork()