一、背景与需求
在开发云相册上传功能时,我们常常需要处理多个照片的连续上传任务。这类任务往往涉及以下几个方面的需求:
- 任务顺序执行:确保上传任务按照预定顺序依次执行。
- 支持动态添加任务:支持在队列中随时添加新的上传任务。
- 处理任务冲突与替换:如果某些任务已经存在,需要有策略来决定是否替换。
- 任务队列可靠性:确保任务队列能在后台持久化执行,并避免中途丢失。
为了满足这些需求,WorkManager
提供了一个强大的解决方案。它不仅支持任务的顺序执行、任务冲突处理,还可以保证任务在应用终止后的持续运行。

二、WorkManager 任务链实现
1. beginUniqueWork 的使用
beginUniqueWork
方法用于开始一个唯一的任务队列。这意味着同一个队列中的任务将会按顺序执行,同时可以避免任务冲突。
objectivec
workManager.beginUniqueWork(
CLOUD_UPLOAD_TASK_QUEUE, // 唯一队列名称
ExistingWorkPolicy.APPEND_OR_REPLACE, // 任务冲突策略
uploadWorkRequest // 上传任务请求
).enqueue()
关键点解析:
CLOUD_UPLOAD_TASK_QUEUE
:定义一个唯一的任务队列,确保所有任务都在同一队列中执行。ExistingWorkPolicy.APPEND_OR_REPLACE
:任务冲突策略,指定了如何处理已经存在的任务。这里使用了APPEND_OR_REPLACE
,表示如果该任务已经存在,则用新任务替换。uploadWorkRequest
:具体的上传任务请求,这个任务会被加入到队列中,等待执行。
2. ExistingWorkPolicy 策略解析
ExistingWorkPolicy
枚举定义了多个任务冲突策略,开发者可以根据需求灵活选择。
kotlin
enum class ExistingWorkPolicy {
// 追加到现有队列
APPEND,
// 追加或替换
APPEND_OR_REPLACE,
// 保持现有任务
KEEP,
// 替换现有任务
REPLACE
}
在云相册上传场景中,我们通常使用 APPEND_OR_REPLACE
策略:
- 支持任务追加:新的上传任务会被追加到现有队列中,保持任务的顺序。
- 处理任务冲突:如果当前队列中已经有相同的任务,新的任务会替换掉旧任务,确保队列中的任务不重复。
- 保证顺序:任务会按照队列顺序执行,避免任务错乱。
3. 实际应用示例
在实际应用中,我们可以封装一个工具类来管理任务队列:
kotlin
object WorkManagerUtils {
private const val CLOUD_UPLOAD_TASK_QUEUE = "CloudUploadTaskQueue"
fun scheduleCloudUploadAndNotify(
context: Context,
recordId: Long,
nowNotify: Boolean = false
) {
val workManager = WorkManager.getInstance(context)
// 创建上传任务
val uploadWorkRequest = OneTimeWorkRequestBuilder<CloudUploadWorker>()
.setInputData(
Data.Builder()
.putLong("recordId", recordId)
.putBoolean("nowNotify", nowNotify)
.build()
)
.build()
// 使用 APPEND_OR_REPLACE 策略添加到队列
workManager.beginUniqueWork(
CLOUD_UPLOAD_TASK_QUEUE,
ExistingWorkPolicy.APPEND_OR_REPLACE,
uploadWorkRequest
).enqueue()
}
}
三、任务队列管理优化
1. 队列状态监控
我们可以通过 WorkManager
提供的 getWorkInfosForUniqueWorkLiveData
方法来实时监控队列状态:
scss
workManager.getWorkInfosForUniqueWorkLiveData(CLOUD_UPLOAD_TASK_QUEUE)
.observe(lifecycleOwner) { workInfoList ->
workInfoList.forEach { workInfo ->
when (workInfo.state) {
WorkInfo.State.SUCCEEDED -> handleSuccess(workInfo)
WorkInfo.State.FAILED -> handleFailure(workInfo)
WorkInfo.State.RUNNING -> updateProgress(workInfo)
}
}
}
这样,我们可以根据任务的状态(如成功、失败或正在运行)来执行相应的处理逻辑。
2. 任务取消和清理
如果需要清空上传队列或取消所有上传任务,可以调用 cancelUniqueWork
来实现:
kotlin
fun cancelUploadQueue(context: Context) {
WorkManager.getInstance(context)
.cancelUniqueWork(CLOUD_UPLOAD_TASK_QUEUE)
}
四、实践中的注意事项
1. 避免队列阻塞
在上传任务失败时,为了避免阻塞整个任务队列,可以在上传失败时直接返回 Result.success()
,并在后台记录失败信息,便于后续重试。
kotlin
override suspend fun doWork(): Result {
return try {
// 上传逻辑
if (uploadSuccess) {
Result.success()
} else {
// 失败后直接返回成功,避免阻塞队列
// 但要记录失败状态,便于后续重试
database.markUploadFailed(recordId)
Result.success()
}
} catch (e: Exception) {
// 严重错误才返回重试
Result.retry()
}
}
2. 任务优先级处理
对于一些需要立即上传的照片,我们可以设置优先级,使其尽快执行:
kotlin
fun scheduleImmediateUpload(context: Context, recordId: Long) {
val uploadWork = OneTimeWorkRequestBuilder<CloudUploadWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setInputData(workDataOf("recordId" to recordId))
.build()
// 使用 REPLACE 策略确保立即执行
workManager.enqueueUniqueWork(
"immediate_upload_$recordId",
ExistingWorkPolicy.REPLACE,
uploadWork
)
}

五、最佳实践建议
1. 队列命名规范
为了避免命名冲突,我们可以定义一个队列命名规则:
kotlin
private const val QUEUE_PREFIX = "cloud_upload_"
private const val QUEUE_NAME = "${QUEUE_PREFIX}main_queue"
2. 任务标识管理
可以通过记录任务的 recordId
来生成唯一的任务标识:
kotlin
fun getUploadWorkName(recordId: Long) = "upload_$recordId"
3. 状态同步处理
为了方便状态同步处理,我们可以使用 StateFlow
来跟踪任务队列的状态:
kotlin
class UploadQueueManager {
private val _queueState = MutableStateFlow<QueueState>(QueueState.Idle)
val queueState: StateFlow<QueueState> = _queueState.asStateFlow()
fun updateQueueState(workInfos: List<WorkInfo>) {
val running = workInfos.any { it.state == WorkInfo.State.RUNNING }
val failed = workInfos.any { it.state == WorkInfo.State.FAILED }
_queueState.value = when {
running -> QueueState.Running
failed -> QueueState.HasFailed
else -> QueueState.Idle
}
}
}
六、总结
通过 WorkManager
的 beginUniqueWork
和 APPEND_OR_REPLACE
策略,我们可以优雅地管理云相册的上传任务队列。关键要点包括:
- 任务顺序执行:确保上传任务按顺序执行,避免乱序。
- 任务失败处理:合理处理任务失败,避免影响后续任务执行。
- 任务队列监控与管理:实时监控任务队列的状态,并进行相应的处理。
- 支持优先级上传需求:支持紧急任务优先上传。
这种方式不仅可以保证任务的顺序执行和可靠性,还能灵活地应对动态任务的添加和优先级管理,构建一个高效且灵活的上传队列系统。