refactor(serial): sendCmd 改用 Channel 保证串口写入顺序

- 新增 sendChannel(无界 Channel)作为发送队列
- openPort() 启动单一消费者协程顺序消费队列,避免并发写串口
- sendCmd() 改为 trySend() 入队,不再每次创建新协程
- release() 关闭 Channel,确保消费者协程正常退出

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 15:02:15 +08:00
co-authored by Claude Sonnet 4.6
parent 7b28eb03d6
commit d5c6ec92d4
@@ -20,6 +20,7 @@ import io.github.jeadyx.jserialport.SerialPortFactory
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -73,6 +74,9 @@ object ScaleManager {
private var serialPort: AndroidSerialPort? = null private var serialPort: AndroidSerialPort? = null
private var scope = CoroutineScope(Dispatchers.IO) private var scope = CoroutineScope(Dispatchers.IO)
/** 发送队列:保证串口写入顺序,避免并发写冲突 */
private val sendChannel = Channel<String>(capacity = Channel.UNLIMITED)
/** 当前设备激活码,非空时收到激活请求自动应答 */ /** 当前设备激活码,非空时收到激活请求自动应答 */
private var activateCode: String = "" private var activateCode: String = ""
@@ -141,6 +145,17 @@ object ScaleManager {
parity = SerialPort.PARITY_NONE parity = SerialPort.PARITY_NONE
) )
} as AndroidSerialPort? } as AndroidSerialPort?
// 启动单一消费者协程,顺序消费发送队列
scope.launch {
for (hexStr in sendChannel) {
try {
serialPort?.write(hexStr.hexToBytes())
} catch (e: Exception) {
e.printStackTrace()
log("发送命令失败: ${e.message}")
}
}
}
true true
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -301,7 +316,7 @@ object ScaleManager {
// ---------------------------------------------------------------- // ----------------------------------------------------------------
/** /**
* 发送十六进制字符串指令(需先调用 init() 注入发送函数) * 发送十六进制字符串指令,指令进入发送队列按顺序写入串口
* *
* @param hexStr 完整帧十六进制字符串,含协议头尾 * @param hexStr 完整帧十六进制字符串,含协议头尾
*/ */
@@ -310,14 +325,7 @@ object ScaleManager {
log("警告:ScaleManager 串口未打开或已释放") log("警告:ScaleManager 串口未打开或已释放")
return return
} }
scope.launch { sendChannel.trySend(hexStr)
try {
serialPort?.write(hexStr.hexToBytes())
} catch (e: Exception) {
e.printStackTrace()
log("发送命令失败: ${e.message}")
}
}
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@@ -423,6 +431,7 @@ object ScaleManager {
* 通常在 Activity/Fragment 销毁或不再使用时调用 * 通常在 Activity/Fragment 销毁或不再使用时调用
*/ */
suspend fun release() { suspend fun release() {
sendChannel.close()
scope.cancel() scope.cancel()
serialPort?.close() serialPort?.close()
serialPort = null serialPort = null