3. 传统蓝牙 API(BluetoothAdapter)基本调用:设备发现、配对流程、Socket 通信代码

3.1 原理概述

Android 传统蓝牙 API 基于 BluetoothAdapter 单例管理本地蓝牙适配器,通过 BluetoothDevice 表示远程设备,BluetoothSocket 建立 RFCOMM 通信通道。核心流程为:设备发现 → 配对 → 建立连接 → 数据收发。该过程遵循 Bluetooth 2.0+ 的 SDP 和 L2CAP/RFCOMM 协议栈。

角色关键类职责
本地适配器BluetoothAdapter启用/关闭蓝牙、开始/取消发现、获取已配对设备列表
远程设备BluetoothDevice封装 MAC 地址、名称、Bonding 状态;发起配对或连接
通信套接字BluetoothSocket客户端发起连接,服务端监听;RFCOMM 流式读写
服务端套接字BluetoothServerSocket监听特定 UUID 的 RFCOMM 通道,返回 BluetoothSocket

3.2 设备发现流程

要点:发现过程会持续约 12 秒,需要注册 BroadcastReceiver 监听 ACTION_FOUND 和 ACTION_DISCOVERY_FINISHED 系统广播。
  1. 获取 BluetoothAdapter.getDefaultAdapter(),检查是否支持且已开启。
  2. 注册 BroadcastReceiver,IntentFilter 包含 BluetoothDevice.ACTION_FOUND。
  3. 调用 mBluetoothAdapter.startDiscovery()(异步)。
  4. 在广播接收器中通过 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) 获取发现的设备。
  5. 发现结束后(ACTION_DISCOVERY_FINISHED),取消注册接收器。
// 设备发现核心代码片段
private final BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // 添加到列表,记录 device.getName() 和 device.getAddress()
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            // 更新 UI 或通知扫描结束
        }
    }
};

// 注册与启动
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(discoveryReceiver, filter);
mBluetoothAdapter.startDiscovery();

3.3 配对流程(Bonding)

配对在 Android 中通常通过系统对话框自动发起或由程序主动调用 device.createBond() 触发。配对状态变化通过广播 ACTION_BOND_STATE_CHANGED 获取。

  • BOND_NONE → 未配对
  • BOND_BONDING → 正在配对
  • BOND_BONDED → 已配对
// 主动发起配对(需要系统权限 BLUETOOTH_ADMIN)
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
    try {
        Method createBondMethod = device.getClass().getMethod("createBond");
        createBondMethod.invoke(device);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
注意:createBond() 是隐藏 API,安卓 10+ 可通过 device.createBond() 公开调用,但仍需用户确认 PIN 码。

3.4 Socket 通信(RFCOMM)

使用 UUID(标准串口服务 00001101-0000-1000-8000-00805F9B34FB)建立 RFCOMM 通道。服务端监听 → 客户端连接 → 双方通过 InputStream 和 OutputStream 收发数据。

步骤服务端(Server)客户端(Client)
1创建 BluetoothServerSocket,调用 listenUsingRfcommWithServiceRecord(name, uuid)获取已配对设备,调用 device.createRfcommSocketToServiceRecord(uuid)
2调用 serverSocket.accept() 阻塞等待连接调用 socket.connect()
3接收成功得到 BluetoothSocket,关闭 ServerSocket连接成功后得到活跃 socket
4通过 socket.getInputStream()/getOutputStream() 读写同左
// 服务端线程示例
private class AcceptThread extends Thread {
    private final BluetoothServerSocket serverSocket;
    public AcceptThread(BluetoothAdapter adapter, String name, UUID uuid) {
        BluetoothServerSocket tmp = null;
        try {
            tmp = adapter.listenUsingRfcommWithServiceRecord(name, uuid);
        } catch (IOException e) { }
        serverSocket = tmp;
    }
    public void run() {
        BluetoothSocket socket = null;
        while (true) {
            try {
                socket = serverSocket.accept();
            } catch (IOException e) { break; }
            if (socket != null) {
                // 在另外线程处理连接
                manageConnectedSocket(socket);
                break;
            }
        }
    }
}

// 客户端连接示例
public class ConnectThread extends Thread {
    private final BluetoothSocket socket;
    public ConnectThread(BluetoothDevice device, UUID uuid) {
        BluetoothSocket tmp = null;
        try {
            tmp = device.createRfcommSocketToServiceRecord(uuid);
        } catch (IOException e) { }
        socket = tmp;
    }
    public void run() {
        mBluetoothAdapter.cancelDiscovery(); // 连接前取消发现
        try {
            socket.connect();
        } catch (IOException e) {
            // 处理连接失败
            try { socket.close(); } catch (IOException closeEx) { }
            return;
        }
        // 连接成功,开始读写
    }
}

3.5 调试与常见错误

  • 错误:not discoverable → 确保已开启可检测性(startActivityForResult 请求 ACTION_REQUEST_DISCOVERABLE)。
  • 错误:Service discovery failed → SDP 查找 UUID 超时;检查 UUID 是否匹配、设备是否在范围内。
  • 错误:read failed, socket might closed → 读写线程未处理连接中断;使用心跳机制。
  • 错误:Socket 连接一直阻塞 → ServerSocket 未正确关闭或 accept 线程未启动。
调试技巧:使用 adb logcat 过滤 BluetoothAdapter、BluetoothSocket 标签;安卓 9+ 需要 ACCESS_FINE_LOCATION 权限用于扫描。

3.6 性能与稳定性优化

  • 发现操作非常耗电,建议仅在有用户交互时执行,发现后立即取消。
  • 连接前务必 cancelDiscovery(),否则连接会延迟或失败。
  • 使用 线程池 管理连接线程,避免频繁创建 Thread。
  • 读写使用 循环缓存 或 独立写队列,防止 UI 线程阻塞。
  • 增加连接超时(通过 socket.connect() 可传入 timeout 方法?实际上 Android 的 connect() 没有参数,可使用 socket.setSoTimeout() 在读写时控制)。

3.7 整体通信流程图

蓝牙设备发现→配对→通信流程 手机A (本地) BluetoothAdapter startDiscovery() 发现设备列表 手机B (远程) 配对 (Bonding) RFCOMM Socket 数据流 流程:发现设备 → 选择设备 → 配对 → 建立RFCOMM Socket → 双向数据通信