android 实现排列管理http请求

125 阅读8分钟

- 一、项目介绍

  1. 背景与应用场景 在移动应用开发中,往往存在大量需要按序执行、互相隔离、限流与限并发的场景,例如:

网络请求排队:前端批量上传图片,避免同时发起大量请求导致服务器拒绝或客户端 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#idstart,willrunid start, will run delayMs ms") val time = measureTimeMillis { delay(delayMs) } Log.d("TaskQueue", "Task#idfinishedinid finished in time ms") return "Result(id)afterid) after {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?>
    ) {}`