WorkManager 任务链详解:优雅处理云相册上传队列

一、背景与需求

在开发云相册上传功能时,我们常常需要处理多个照片的连续上传任务。这类任务往往涉及以下几个方面的需求:

  • 任务顺序执行:确保上传任务按照预定顺序依次执行。
  • 支持动态添加任务:支持在队列中随时添加新的上传任务。
  • 处理任务冲突与替换:如果某些任务已经存在,需要有策略来决定是否替换。
  • 任务队列可靠性:确保任务队列能在后台持久化执行,并避免中途丢失。

为了满足这些需求,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
        }
    }
}

六、总结

通过 WorkManagerbeginUniqueWorkAPPEND_OR_REPLACE 策略,我们可以优雅地管理云相册的上传任务队列。关键要点包括:

  • 任务顺序执行:确保上传任务按顺序执行,避免乱序。
  • 任务失败处理:合理处理任务失败,避免影响后续任务执行。
  • 任务队列监控与管理:实时监控任务队列的状态,并进行相应的处理。
  • 支持优先级上传需求:支持紧急任务优先上传。

这种方式不仅可以保证任务的顺序执行和可靠性,还能灵活地应对动态任务的添加和优先级管理,构建一个高效且灵活的上传队列系统。

原文地址:mp.weixin.qq.com/s/eygq5vQor...

相关推荐
安东尼肉店6 小时前
Android compose屏幕适配终极解决方案
android
2501_916007477 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun8 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户20187928316712 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子12 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜822712 小时前
安卓接入Max广告源
android
齊家治國平天下12 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO12 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel12 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢12 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱