You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同一协程队列操作:蓝牙异步接收自定义协议数据如何按序解码

你现有代码的核心问题是每次收到新数据都会启动独立协程,多个协程并行执行时,即使加了Mutex也只能保证解码逻辑互斥,无法保证协程执行顺序和数据到达顺序一致,会出现后到的数据包先被处理的情况。

推荐采用「Channel做缓冲 + 单工作协程串行消费」的方案,天然保证处理顺序,不需要额外加锁,逻辑更简单稳定。


最终修改代码

ViewModel 层修改

// ViewModel
private var coroutineJob = Job()
// 工作协程绑定Default调度器,所有解码逻辑在后台执行
private val workScope = CoroutineScope(Dispatchers.Default + coroutineJob)
// 数据缓冲通道,容量可根据实际场景调整,蓝牙场景用UNLIMITED足够
private val dataChannel = Channel<ByteArray>(Channel.UNLIMITED)

init {
    // 启动唯一消费者协程,按入队顺序处理所有数据
    workScope.launch {
        for (bytes in dataChannel) {
            // 上一个数据包处理完成后才会取下一个,天然串行
            Packet.processMessage(bytes.toUByteArray()).collect { decodeResult ->
                updateData(decodeResult)
            }
        }
    }
}

fun getData(bytes: ByteArray) {
    // 主线程收到数据直接入队,非阻塞不会卡UI
    dataChannel.trySend(bytes)
}

override fun onCleared() {
    super.onCleared()
    // 页面销毁时释放资源,避免内存泄漏
    coroutineJob.cancel()
    dataChannel.close()
}

Packet 单例修改

因为现在已经是串行调用解码逻辑,不需要额外加Mutex锁,直接删掉锁逻辑即可:

object Packet {
    suspend fun processMessage(buffer: UByteArray) = flow {
        decode(buffer).collect { emit(it) }
    }
    private fun decode(buffer: UByteArray) = channelFlow {
        // 原有解码逻辑无需修改
        offer()
    }
}

优化说明

  • 所有解码逻辑都在后台线程执行,不会阻塞主线程
  • 单消费者协程严格按照数据到达顺序处理,完全避免了并行解码的时序问题
  • 通道的trySend是非阻塞操作,符合LiveData回调在主线程执行的场景要求
  • 如果需要处理背压,可以调整Channel的容量和溢出策略,比如设置容量为64、溢出策略为BufferOverflow.DROP_OLDEST

内容的提问来源于stack exchange,提问作者Pico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 16:30:01