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

如何在同一应用程序内连接两台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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:52:40