- 一、项目介绍
- 背景与应用场景 在移动应用开发中,往往存在大量需要按序执行、互相隔离、限流与限并发的场景,例如:
网络请求排队:前端批量上传图片,避免同时发起大量请求导致服务器拒绝或客户端 OOM
数据库事务:先后写入多条记录,保证写入顺序与隔离
耗时计算:音视频转码、图片压缩等需要顺序处理而非并发
UI 事件节流:用户快速多次点击按钮时,将事件排队依次执行防抖
为了统一管理这些异步任务,减少回调地狱、控制并发数量、保证顺序执行,我们需要一个通用的排队任务管理组件(Task Queue/Task Scheduler),支持:
顺序执行:所有任务按入队顺序依次执行
并发控制:可配置最大并发数或并发策略
失败重试:支持任务失败后的自动重试或回调处理
超时控制:每个任务可配置超时时间,超时后自动取消或重试
生命周期感知:结合 Activity/Fragment/Service 生命周期,自动取消队列
多种实现方案:基于 HandlerThread、Executors、RxJava、Kotlin 协程、WorkManager 等
本文将系统地呈现从基础原理、多种实现思路到封装组件、性能优化、扩展应用的全流程,并提供一份可直接使用的 TaskQueueManager 模板代码。
二、相关知识
在落地排队任务组件之前,需要掌握以下核心技术点:
线程与消息循环
HandlerThread + Handler:简单易用的单线程异步执行
ExecutorService:线程池执行任务,支持并发与限流
RxJava
Observable.concat()、flatMapSequential() 实现顺序流
Schedulers.single()、Schedulers.io() 控制执行线程
Kotlin 协程
CoroutineScope、launch、async
Channel/Actor 模式构建顺序队列
Semaphore 控制并发数
WorkManager
适合持久化、延迟、后台执行的任务队列
OneTimeWorkRequest 链式执行
Android JobScheduler
系统级调度,适合设备空闲/网络变化时执行任务
生命周期
Activity/Fragment onDestroy() 时取消队列
Service onDestroy()、onUnbind() 等
三、实现思路
本文将介绍以下几种实现方式,最后统一封装为 TaskQueueManager:
HandlerThread 队列
在单独的 HandlerThread 上依次 post(Runnable)
支持延迟与超时
ExecutorService 顺序执行
使用单线程 Executors.newSingleThreadExecutor() 或固定大小线程池
对每个任务封装 Future,支持取消与超时
RxJava 队列
PublishSubject 入队,将任务映射为 Single,用 flatMapSequential 顺序执行
Kotlin 协程 + Channel
创建一个 Actor(背后是协程 Channel),按顺序消费消息并执行任务
使用 Semaphore 控制并发度
WorkManager 链式任务
将一系列 OneTimeWorkRequest 链接为 WorkContinuation,在后台持久执行
最终,我们将用协程 + Actor的方式,实现一个高性能、可配置、生命周期感知的排队任务管理器 TaskQueueManager。
四、环境与依赖 // app/build.gradle plugins { id 'com.android.application' id 'kotlin-android' }
android { compileSdkVersion 34 defaultConfig { applicationId "com.example.taskqueue" minSdkVersion 21 targetSdkVersion 34 } buildFeatures { viewBinding true } kotlinOptions { jvmTarget = "1.8" } }
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" implementation 'io.reactivex.rxjava3:rxjava:3.1.5' implementation 'androidx.work:work-runtime-ktx:2.8.0' } 五、整合代码 // ======================================================= // 文件:TaskQueueManager.kt // 描述:使用协程 Actor 实现的通用排队任务管理器 // ======================================================= package com.example.taskqueue
import android.util.Log import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.actor import java.util.concurrent.atomic.AtomicBoolean import kotlin.system.measureTimeMillis
/**
- 通用任务队列管理器
- 支持:顺序执行、并发控制、超时、重试、生命周期感知 */ class TaskQueueManager( private val scope: CoroutineScope, // 作用域,通常为 lifecycleScope private val concurrency: Int = 1, // 最大并发数量 private val defaultTimeoutMs: Long = 0L // 默认超时(ms),<=0 表示无限制 ) : LifecycleObserver {
// Actor 消息类型 sealed class Msg { data class Submit( val task: suspend () -> T, val callback: (Result) -> Unit, val timeoutMs: Long = defaultTimeoutMs, val retries: Int = 0 ) : Msg() object Shutdown : Msg() }
// 创建 Actor,capacity 可控制缓冲 private val actor = scope.actor(capacity = Channel.UNLIMITED) { val semaphore = Semaphore(concurrency) for (msg in channel) { when (msg) { is Msg.Submit<*> -> { launch { semaphore.acquire() process(msg) semaphore.release() } } Msg.Shutdown -> { break } } } }
// 处理单个提交任务:超时 & 重试 private suspend fun process(msg: Msg.Submit) { var attempt = 0 while (true) { if (msg.timeoutMs > 0) { try { val result = withTimeout(msg.timeoutMs) { msg.task() } msg.callback(Result.success(result)) return } catch (e: Exception) { attempt++ if (attempt > msg.retries) { msg.callback(Result.failure(e)) return } } } else { try { val result = msg.task() msg.callback(Result.success(result)) return } catch (e: Exception) { attempt++ if (attempt > msg.retries) { msg.callback(Result.failure(e)) return } } } } }
/**
- 提交任务到队列
- @param task 挂起函数,返回结果
- @param timeoutMs 超时时间(ms)
- @param retries 重试次数
- @param callback 执行完毕时回调 */ fun submit( timeoutMs: Long = defaultTimeoutMs, retries: Int = 0, task: suspend () -> T, callback: (Result) -> Unit ) { actor.trySend(Msg.Submit(task, callback, timeoutMs, retries)) }
/** 关闭队列,取消所有未执行任务 */ fun shutdown() { actor.trySend(Msg.Shutdown) }
/** 与生命周期绑定,在 onDestroy 时自动关闭 */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { shutdown() } }
// ======================================================= // 文件:ExampleActivity.kt // 描述:示例页面,演示 TaskQueueManager 用法 // ======================================================= package com.example.taskqueue
import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import com.example.taskqueue.databinding.ActivityExampleBinding import kotlinx.coroutines.delay import kotlin.random.Random
class ExampleActivity : AppCompatActivity() { private lateinit var binding: ActivityExampleBinding private lateinit var queue: TaskQueueManager
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root)
// 初始化队列:并发 2,默认超时 5s
queue = TaskQueueManager(lifecycleScope, concurrency = 2, defaultTimeoutMs = 5000)
lifecycle.addObserver(queue)
binding.btnSubmit.setOnClickListener {
binding.tvLog.text = ""
// 提交 5 个示例任务
repeat(5) { idx ->
queue.submit(
retries = 1,
task = { sampleTask(idx) },
callback = { res ->
runOnUiThread {
binding.tvLog.append("Task #$idx -> ${res.getOrElse { it.message }}\n")
}
}
)
}
}
}
/** 模拟耗时任务:随机延迟,可能超时 */ private suspend fun sampleTask(id: Int): String { val delayMs = Random.nextLong(1000, 8000) Log.d("TaskQueue", "Task#delayMs ms") val time = measureTimeMillis { delay(delayMs) } Log.d("TaskQueue", "Task#time ms") return "Result({time}ms" } }
// ======================================================= // 文件:res/layout/activity_example.xml // =======================================================
六、代码解读 核心组件 TaskQueueManager
采用协程 Actor模型,实现消息驱动的任务队列;
内部使用 Semaphore 限制并发数;
每个任务封装成 Msg.Submit,携带:挂起函数、回调、超时、重试次数;
process() 方法通过 withTimeout 实现超时,通过循环尝试实现重试;
提供 submit()、shutdown() 方法,并可绑定 Lifecycle 自动在 onDestroy() 时关闭队列。
示例页面 ExampleActivity
在 onCreate 初始化队列并绑定生命周期;
点击按钮后提交 5 个模拟任务,每个任务随机延迟 1–8 秒;
由于默认超时 5 秒且重试 1 次,部分任务可能在两次尝试后依然超时;
回调在主线程更新日志,用于查看实际执行顺序与结果。
并发与顺序
Actor 接收消息立即派发子协程执行,且有 Semaphore(2),保证最多 2 个任务并发;
任务完成后释放信号量,自动取队列下一个任务执行。
扩展性
可在 Msg.Submit 内增加优先级字段,实现优先级队列;
可改造为有界容量的 Channel,避免内存无限增长;
可在 Actor 里监听特定系统广播,实现条件触发再执行。
七、性能与优化 协程轻量
相较于线程,协程创建与切换开销低,适合大量小任务。
限流 & 限速
Semaphore 控制并发数,避免过度争抢资源;
可在 process() 前后统计执行时间,实现节流或限速策略。
超时与取消
基于 withTimeout 实现超时,避免单个任务长时间挂起队列;
shutdown() 不再接收新任务,未执行的消息可选择丢弃或立即回调失败。
Lifecycle 绑定
与 Activity/Fragment 绑定,通过 LifecycleObserver 自动释放,防止内存泄漏;
若需跨页面共享,可用 ViewModelScope 或自定义 ApplicationScope。
八、项目总结与拓展
本文从排队任务的需求与场景出发,深入讲解了基于协程 Actor的队列实现,结合超时、重试、并发控制与生命周期感知,最终封装成 TaskQueueManager。这一组件可广泛应用于网络限流、数据库写入、后台批处理、UI 事件节流等多种场景。
拓展思路 优先级队列:在 Msg 中增加 priority 字段,使用 PriorityChannel 实现高优先级先执行
任务依赖:支持任务 A 完成后再执行任务 B,构建有向无环图(DAG)执行器
任务持久化:失败重启后保持队列状态,可结合 Room 或 WorkManager
分布式队列:将任务调度与执行分离,前端入队后交由后端或 Service 框架执行
调度策略:根据网络状态、设备空闲程度动态启动或暂停队列
九、FAQ Q1:Actor 与 Channel 有何区别?为什么用 Actor? A1:Actor 是“处理消息的协程”,可以封装状态与消息循环逻辑;Channel 只是通道,用于传输数据,更低阶。
Q2:为什么不直接用 newSingleThreadExecutor().submit()? A2:Executor 无法方便地实现超时、重试、Lifecycle 绑定,协程方案更灵活。
Q3:如何取消正在执行的任务? A3:可在 process() 中捕获 CancellationException,并在回调中告知任务已取消。
Q4:队列中的任务执行异常怎么办? A4:在 process() 中捕获异常,并通过回调返回 Result.failure(e),同时根据 retries 决定是否重新入队。
Q5:队列关闭后,如何再重启? A5:shutdown() 会关闭 Actor,若需重启请重新创建一个新的 TaskQueueManager 实例。
————————————————
原文链接:blog.csdn.net/m0_61840987…
其他案例kotlin协程实现 适配器
import androidx.databinding.BindingAdapter
import androidx.databinding.ObservableArrayList
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.purui.mobile.R
import com.purui.mobile.base.BaseBindingAdapter
import com.purui.mobile.databinding.AdapterRequestQueueIncomingItemBinding
import com.purui.mobile.manager.queue.QueueRequestItem
import com.purui.mobile.ui.main.model.BarcodeEpcQueueMessage
/**
* 请求队列列表
*/
class RequestQueueIncomingListAdapter(items: ObservableArrayList<QueueRequestItem<RequestQueueIncomingListAdapterModel>>) :
BaseBindingAdapter<QueueRequestItem<RequestQueueIncomingListAdapterModel>, AdapterRequestQueueIncomingItemBinding>(
items
) {
var onItemClick: ((QueueRequestItem<RequestQueueIncomingListAdapterModel>) -> Unit)? = null
override fun getLayoutResId() = R.layout.adapter_request_queue_incoming_item
override fun onBindItem(
binding: AdapterRequestQueueIncomingItemBinding?,
bean: QueueRequestItem<RequestQueueIncomingListAdapterModel>,
position: Int
) {
binding?.root?.setOnClickListener {
onItemClick?.invoke(bean)
}
binding?.model = bean
}
}
@BindingAdapter(
value = ["RequestQueueIncomingListAdapter_bindlist",
"RequestQueueIncomingListAdapter_onItemClick"], requireAll = false
)
fun RequestQueueIncomingListAdapter_binding(
view: RecyclerView,
list: ObservableArrayList<QueueRequestItem<RequestQueueIncomingListAdapterModel>>,
onItemClick: ((QueueRequestItem<RequestQueueIncomingListAdapterModel>) -> Unit)? = null,
) {
if (view.adapter != null) {
return
}
val adapter = RequestQueueIncomingListAdapter(list)
adapter.onItemClick = onItemClick
view.adapter = adapter
view.layoutManager = LinearLayoutManager(view.context)
}
data class RequestQueueIncomingListAdapterModel(
val name: String,
val list: List<BarcodeEpcQueueMessage>,
草稿单号
val REFNO: String? = null
)
队列管理器:QueueRequestManager
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableField
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.purui.mobile.utils.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Description: 队列请求管理器 , 消息排除处理
*/
class QueueRequestManager<T,R> {
val queue = ObservableArrayList<QueueRequestItem<T>>()
private var running = false
//协程做循环执行队列
fun startLoop(viewModel: ViewModel, onTaskRun: OnTaskRunListener<T,R>) {
if (running) return
running = true
viewModel.viewModelScope.launch {
while (running) {
val item = queue.firstOrNull()
if (item == null) {
delay(100)
continue
}
item.status.set(RequestStatus.Requesting)
onTaskRun.onStart(item.data)
val result = withContext(Dispatchers.IO) {//执行结果
if (QueueRequestDebugParam.debug.get()) { delay(1000) }
onTaskRun.onTaskRun(item.data)
}
item.status.set(result.status)
item.msg.set(result.msg)
onTaskRun.onFinish(item.data, result)
queue.remove(item)
}
}
}
fun cancelLoop() {
running = false
}
fun clear() {
queue.clear()
}
fun pushMessage(message:QueueRequestItem<T>) {
queue.add(message)
LogUtils.d("pushMessage=", "queue.size=${queue.size}")
}}
data class QueueRequestItem<T>(val status:ObservableField<RequestStatus>, val msg:ObservableField<String>, val data:T)
enum class RequestStatus {
Wait,
Requesting,
Success,
Failed
}
interface OnTaskRunListener<T,R> {
fun onStart(message:T)
fun onTaskRun(message:T):RequestResult<R>
fun onFinish(message:T, result:RequestResult<R>)
}
data class RequestResult<T>(val status:RequestStatus, val msg:String, val data:T)
实现
mQueueRequestManager.startLoop(viewModel = this, onTaskRun = object :
OnTaskRunListener<RequestQueueIncomingListAdapterModel, Any?> {
override fun onStart(message: RequestQueueIncomingListAdapterModel) {
}
override fun onTaskRun(message: RequestQueueIncomingListAdapterModel): RequestResult<Any?> {
}
override fun onFinish(
message: RequestQueueIncomingListAdapterModel,
result: RequestResult<Any?>
) {}`