如何在同一应用程序内连接两台Bluetooth SPP设备?
在同一Android应用中连接两台Bluetooth SPP设备的实现方案
我懂你为啥头疼——BluetoothChat示例确实只做了一对一的SPP连接,要同时连两台设备得自己扩展逻辑。我之前做过类似需求,整理了一套可行的方案,你可以参考:
核心思路
Bluetooth SPP基于RFCOMM协议,每个连接需要独立的BluetoothSocket和对应的线程来处理数据收发。关键是为每台设备单独维护一套连接管理逻辑,避免线程和Socket的冲突。
1. 先搞定设备扫描与配对
- 用
BluetoothAdapter扫描周围的SPP设备,获取每台设备的BluetoothDevice实例 - 检查设备配对状态:调用
device.getBondState(),如果未配对,触发系统配对流程(比如通过device.createBond())
2. 为每台设备创建独立的连接线程
不能复用BluetoothChat里的单个连接线程,要为每台设备写一个专属的连接线程类:
private class MultiConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; private final String mmDeviceTag; // 用设备名或MAC区分不同设备 public MultiConnectThread(BluetoothDevice device) { mmDevice = device; mmDeviceTag = device.getName() + "(" + device.getAddress() + ")"; BluetoothSocket tmpSocket = null; // SPP设备通用UUID,大部分设备都用这个 String sppUuid = "00001101-0000-1000-8000-00805F9B34FB"; try { tmpSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(sppUuid)); } catch (IOException e) { Log.e(TAG, "创建Socket失败:" + mmDeviceTag, e); } mmSocket = tmpSocket; } @Override public void run() { // 连接前取消扫描,节省资源 bluetoothAdapter.cancelDiscovery(); try { mmSocket.connect(); // 连接成功后启动数据收发线程 startDataThread(mmSocket, mmDeviceTag); } catch (IOException connectErr) { try { mmSocket.close(); } catch (IOException closeErr) { Log.e(TAG, "关闭失败的Socket:" + mmDeviceTag, closeErr); } return; } } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "取消连接失败:" + mmDeviceTag, e); } } }
3. 维护多设备的收发线程
用一个HashMap来存储每台设备对应的收发线程,方便后续管理和区分数据来源:
// 全局存储连接中的设备线程 private HashMap<String, ConnectedThread> connectedDeviceThreads = new HashMap<>(); private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; private final String mmDeviceTag; public ConnectedThread(BluetoothSocket socket, String deviceTag) { mmSocket = socket; mmDeviceTag = deviceTag; InputStream tmpIn = null; OutputStream tmpOut = null; try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "创建数据流失败:" + mmDeviceTag, e); } mmInStream = tmpIn; mmOutStream = tmpOut; } @Override public void run() { byte[] buffer = new byte[1024]; int bytesRead; while (true) { try { bytesRead = mmInStream.read(buffer); String receivedData = new String(buffer, 0, bytesRead); // 这里可以通过mmDeviceTag区分是哪台设备发来的数据 Log.d(TAG, "收到[" + mmDeviceTag + "]的数据:" + receivedData); // 把数据传到UI线程处理(比如用Handler) handler.obtainMessage(MSG_RECEIVE_DATA, bytesRead, -1, new DeviceData(mmDeviceTag, receivedData)).sendToTarget(); } catch (IOException e) { Log.e(TAG, "与[" + mmDeviceTag + "]断开连接", e); // 断开后从HashMap中移除线程 connectedDeviceThreads.remove(mmDeviceTag); break; } } } // 给指定设备发送数据的方法 public void sendData(byte[] data) { try { mmOutStream.write(data); Log.d(TAG, "向[" + mmDeviceTag + "]发送数据:" + new String(data)); } catch (IOException e) { Log.e(TAG, "向[" + mmDeviceTag + "]发送数据失败", e); } } public void disconnect() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "断开[" + mmDeviceTag + "]连接失败", e); } } } // 辅助类:封装设备标识和数据 private static class DeviceData { public String deviceTag; public String data; public DeviceData(String tag, String data) { this.deviceTag = tag; this.data = data; } }
4. 关键注意事项
- 线程隔离:所有连接、读写操作都要放在子线程,绝对不能在主线程执行,否则会触发ANR
- UUID正确性:大部分SPP设备使用通用UUID
00001101-0000-1000-8000-00805F9B34FB,如果设备有自定义UUID,需要替换成对应值 - 资源释放:断开连接时,一定要关闭Socket并从HashMap中移除对应线程,避免内存泄漏
- 错误处理:要捕获连接失败、读写异常等情况,及时给用户反馈(比如Toast提示)
内容的提问来源于stack exchange,提问作者Pink Jazz
相关产品推荐
相关产品推荐

