https://mp.weixin.qq.com/s/eygq5vQor9uMEfpImtbVpg
在开发云相册上传功能时,我们常常需要处理多个照片的连续上传任务。这类任务往往涉及以下几个方面的需求:
任务顺序执行:确保上传任务按照预定顺序依次执行。
支持动态添加任务:支持在队列中随时添加新的上传任务。
处理任务冲突与替换:如果某些任务已经存在,需要有策略来决定是否替换。
任务队列可靠性:确保任务队列能在后台持久化执行,并避免中途丢失。
为了满足这些需求,
WorkManager 提供了一个强大的解决方案。它不仅支持任务的顺序执行、任务冲突处理,还可以保证任务在应用终止后的持续运行。
beginUniqueWork 方法用于开始一个唯一的任务队列。这意味着同一个队列中的任务将会按顺序执行,同时可以避免任务冲突。
workManager.beginUniqueWork( CLOUD_UPLOAD_TASK_QUEUE, // 唯一队列名称 ExistingWorkPolicy.APPEND_OR_REPLACE, // 任务冲突策略 uploadWorkRequest // 上传任务请求 ).enqueue()
关键点解析:
CLOUD_UPLOAD_TASK_QUEUE:定义一个唯一的任务队列,确保所有任务都在同一队列中执行。
ExistingWorkPolicy.APPEND_OR_REPLACE:任务冲突策略,指定了如何处理已经存在的任务。这里使用了
APPEND_OR_REPLACE,表示如果该任务已经存在,则用新任务替换。
uploadWorkRequest:具体的上传任务请求,这个任务会被加入到队列中,等待执行。
ExistingWorkPolicy 枚举定义了多个任务冲突策略,开发者可以根据需求灵活选择。
enum class ExistingWorkPolicy {
// 追加到现有队列
APPEND,
// 追加或替换
APPEND_OR_REPLACE,
// 保持现有任务
KEEP,
// 替换现有任务
REPLACE
}
在云相册上传场景中,我们通常使用
APPEND_OR_REPLACE 策略:
支持任务追加:新的上传任务会被追加到现有队列中,保持任务的顺序。
处理任务冲突:如果当前队列中已经有相同的任务,新的任务会替换掉旧任务,确保队列中的任务不重复。
保证顺序:任务会按照队列顺序执行,避免任务错乱。
在实际应用中,我们可以封装一个工具类来管理任务队列:
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()
}
}
我们可以通过
WorkManager 提供的
getWorkInfosForUniqueWorkLiveData 方法来实时监控队列状态:
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)
}
}
}
这样,我们可以根据任务的状态(如成功、失败或正在运行)来执行相应的处理逻辑。
如果需要清空上传队列或取消所有上传任务,可以调用
cancelUniqueWork 来实现:
fun cancelUploadQueue(context: Context) {
WorkManager.getInstance(context)
.cancelUniqueWork(CLOUD_UPLOAD_TASK_QUEUE)
}
在上传任务失败时,为了避免阻塞整个任务队列,可以在上传失败时直接返回
Result.success(),并在后台记录失败信息,便于后续重试。
override suspend fun doWork(): Result {
return try {
// 上传逻辑
if (uploadSuccess) {
Result.success()
} else {
// 失败后直接返回成功,避免阻塞队列
// 但要记录失败状态,便于后续重试
database.markUploadFailed(recordId)
Result.success()
}
} catch (e: Exception) {
// 严重错误才返回重试
Result.retry()
}
}
对于一些需要立即上传的照片,我们可以设置优先级,使其尽快执行:
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
)
}
为了避免命名冲突,我们可以定义一个队列命名规则:
private const val QUEUE_PREFIX = "cloud_upload_"
private const val QUEUE_NAME = "${QUEUE_PREFIX}main_queue"
可以通过记录任务的
recordId 来生成唯一的任务标识:
fun getUploadWorkName(recordId: Long) = "upload_$recordId"
为了方便状态同步处理,我们可以使用
StateFlow 来跟踪任务队列的状态:
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 策略,我们可以优雅地管理云相册的上传任务队列。关键要点包括:
任务顺序执行:确保上传任务按顺序执行,避免乱序。
任务失败处理:合理处理任务失败,避免影响后续任务执行。
任务队列监控与管理:实时监控任务队列的状态,并进行相应的处理。
支持优先级上传需求:支持紧急任务优先上传。
这种方式不仅可以保证任务的顺序执行和可靠性,还能灵活地应对动态任务的添加和优先级管理,构建一个高效且灵活的上传队列系统。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69917874/viewspace-3058495/ ,如需转载,请注明出处,否则将追究法律责任。