Android蓝牙Socket读取InputStream时字符串被拆分问题
解决方案:通过消息边界处理流式读取拆分问题
蓝牙RFCOMM基于TCP协议,属于流式传输,本身不保证一次能读取到完整的单行指令——数据会被拆分成若干数据包传输,这是协议特性,不是代码bug。要解决这个问题,必须在通信两端约定消息边界,然后在Android端缓存读取到的数据,直到凑出完整的消息。
步骤1:在Arduino端约定消息边界
修改Arduino的发送逻辑,每条指令末尾添加换行符\n(或\r\n,根据串口配置调整),比如:
// 用println自带换行符,直接满足需求 Serial.println("LED On"); // 或者手动添加边界符 Serial.print("Servo Center\n");
步骤2:Android端实现缓存式读取
用StringBuilder缓存每次读取到的字节,每次读取后检查是否包含边界符,拆分出完整消息,剩余部分继续缓存。示例代码如下:
val buffer = ByteArray(1024) var numBytes: Int val messageBuffer = StringBuilder() // 缓存未完成的消息片段 inputStream = mSocket.inputStream while (mSocket.isConnected) { numBytes = inputStream.read(buffer) if (numBytes <= 0) break // 连接断开或读取失败 // 将字节转成字符串,指定UTF-8避免乱码 val received = String(buffer, 0, numBytes, Charsets.UTF_8) messageBuffer.append(received) // 循环检查并提取完整消息 var newlineIndex: Int while (messageBuffer.indexOf('\n').also { newlineIndex = it } != -1) { // 提取完整消息,去掉换行符和首尾空白 val fullMessage = messageBuffer.substring(0, newlineIndex).trim() // 发送给Handler处理 mHandler.obtainMessage(HandlerConstant.MESSAGE_READ, fullMessage).sendToTarget() // 移除已处理的内容,保留未完成的片段 messageBuffer.delete(0, newlineIndex + 1) } }
为什么之前的方法无效?
- 基础
read():仅读取当前输入流中可用的字节,流式传输下无法保证一次拿到完整消息,这是正常行为。 available():仅返回当前已到达的字节数,无法预判后续是否还有数据(长消息可能分批次到达),会导致漏读或拆分。buffered():仅提供字节缓冲能力,没有处理消息边界的逻辑,依然会在缓冲区满或数据流中断时返回部分内容。
额外注意事项
- 确保Android和Arduino使用相同的字符编码(推荐UTF-8),避免乱码。
- 如果用
\r\n作为边界符,检查时要匹配完整的\r\n而非单个\n。 - 线程中要捕获
IOException(连接断开时会抛出),避免程序崩溃。
内容的提问来源于stack exchange,提问作者user22239162
相关产品推荐
相关产品推荐

