Android通过服务UUID连接BLE设备遇IO错误,求解决方案
问题分析与解决方案
错误原因
你使用的createRfcommSocketToServiceRecord()是**经典蓝牙(BR/EDR)**的API,用于连接支持RFCOMM串口服务的设备,但BLE设备基于GATT协议工作,两类协议栈不兼容,因此必然抛出连接超时/失败的异常。
正确的BLE设备连接(通过服务UUID)
要通过服务UUID连接BLE设备,必须使用Android官方提供的BLE GATT API,完整流程如下:
1. 配置权限
在AndroidManifest.xml中添加蓝牙相关权限:
<!-- 低版本蓝牙权限(Android 11及以下) --> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <!-- Android 12+ 蓝牙扫描/连接权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 声明设备支持BLE --> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
2. 初始化BLE适配器
获取蓝牙适配器并确保蓝牙已开启:
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val bluetoothAdapter = bluetoothManager.adapter // 检查蓝牙状态,未开启则请求用户授权开启 if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled) { val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) // REQUEST_ENABLE_BT为自定义请求码,如1 }
3. 扫描含目标服务UUID的BLE设备
启动扫描时指定服务UUID过滤,只获取包含目标服务的设备:
val targetServiceUuid = DeviceProfile.UUID_SERVICE // 你的目标服务UUID val scanFilter = ScanFilter.Builder() .setServiceUuid(ParcelUuid(targetServiceUuid)) .build() val scanSettings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 根据场景选择扫描模式 .build() // 启动扫描 bluetoothAdapter.bluetoothLeScanner.startScan( listOf(scanFilter), scanSettings, scanCallback )
4. 处理扫描结果并发起GATT连接
实现ScanCallback捕获扫描到的设备,停止扫描并发起连接:
private val scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { super.onScanResult(callbackType, result) // 找到目标设备,立即停止扫描 bluetoothAdapter.bluetoothLeScanner.stopScan(this) // 发起GATT连接,第三个参数为连接回调 result.device.connectGatt(this@YourActivity, false, gattCallback) } }
5. 处理GATT连接与服务发现
实现BluetoothGattCallback处理连接状态、服务发现等关键事件:
private val gattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { super.onConnectionStateChange(gatt, status, newState) when (newState) { BluetoothProfile.STATE_CONNECTED -> { // 连接成功,触发服务发现 gatt.discoverServices() } BluetoothProfile.STATE_DISCONNECTED -> { // 连接断开,可处理重连逻辑或资源清理 gatt.close() } } } override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { super.onServicesDiscovered(gatt, status) if (status == BluetoothGatt.GATT_SUCCESS) { // 获取目标服务 val targetService = gatt.getService(DeviceProfile.UUID_SERVICE) targetService?.let { service -> // 可在此操作服务下的特征值(读、写、设置通知等) val targetCharacteristic = service.getCharacteristic(UUID.fromString("你的特征值UUID")) // 示例:读取特征值 gatt.readCharacteristic(targetCharacteristic) } } } }
关键澄清
你提到的“在经典蓝牙之上的GATT连接”存在概念误解:
- GATT(通用属性配置文件)是BLE专属协议,经典蓝牙(BR/EDR)不支持GATT,两者属于独立的蓝牙协议栈,无法混合使用。
- 若你的设备同时支持经典蓝牙和BLE,需明确其提供的服务类型:BLE服务用GATT API连接,经典蓝牙RFCOMM服务才用
createRfcommSocketToServiceRecord()。
内容的提问来源于stack exchange,提问作者Kousik Das
相关产品推荐
相关产品推荐

