Android 11-14蓝牙连接报错:java.io.IOException: read failed求助
Android蓝牙连接ODB2设备抛出
read failed错误排查 错误信息
java.io.IOException: read failed, socket might closed or timeout, read ret: -1 at android.bluetooth.BluetoothSocket.readAll(BluetoothSocket.java:816) at android.bluetooth.BluetoothSocket.readInt(BluetoothSocket.java:830) at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:442)
连接代码(基于官方示例)
class ConnectThread(private val device: BluetoothDevice,private val bluetoothAdapter: BluetoothAdapter?) : Thread() { private val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) { device.createRfcommSocketToServiceRecord(uuid) } override fun run() { bluetoothAdapter?.cancelDiscovery() mmSocket?.use { socket -> socket.connect() } } fun cancel() { try { mmSocket?.close() } catch (e: IOException) { Log.e("TAG", "Could not close the client socket", e) } } }
AndroidManifest权限配置
<!--BLUETOOTH PERMISSION--> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-feature android:name="android.hardware.bluetooth" android:required="false"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/> <uses-feature android:name="android.hardware.bluetooth" android:required="false"/> <!--LOCATION PERMISSION--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
已尝试但无效的方案
- 测试多个静态UUID(
fa87c0d0-afac-11de-8a39-0800200c9a66、00001101-0000-1000-8000-00805F9B34FB等) - 每次连接生成新UUID
- 针对不同设备类型使用对应UUID
- 更换多种蓝牙设备(电视、PC、音响等)测试
- 在Android API 29-34版本的设备上验证
- 切换使用
createRfcommSocketToServiceRecord和createInsecureRfcommSocketToServiceRecord
每次连接均会触发错误,设备显示连接几秒后断开。
可行的排查/解决方向
1. 强制指定RFCOMM通道号
多数ODB2设备使用固定的RFCOMM通道(常见为通道1),绕过UUID匹配逻辑,直接通过反射创建指定通道的Socket:
private fun createRfcommSocket(device: BluetoothDevice, channel: Int): BluetoothSocket? { return try { val method = device.javaClass.getMethod("createRfcommSocket", Int::class.java) method.invoke(device, channel) as BluetoothSocket } catch (e: Exception) { Log.e("TAG", "Failed to create RFCOMM socket", e) null } }
在ConnectThread中替换Socket初始化逻辑,依次尝试通道1、2、3等常见值。
2. 增加连接超时控制
为Socket设置超时时间,避免无限阻塞,同时捕获超时异常做处理:
override fun run() { bluetoothAdapter?.cancelDiscovery() mmSocket?.let { socket -> try { socket.soTimeout = 10000 // 设置10秒超时 socket.connect() // 连接成功后的业务逻辑 } catch (e: java.net.SocketTimeoutException) { Log.e("TAG", "Connection timed out", e) socket.close() } catch (e: IOException) { Log.e("TAG", "Connection failed", e) socket.close() } } }
3. 确认设备配对状态
确保ODB2设备已完成系统级配对,且配对时正确输入PIN码(ODB2设备常用PIN:0000或1234)。未正确配对的设备会出现连接后秒断的情况。
4. 修正权限适配逻辑
- Android 12+(API 31+)需动态申请
BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限,仅在Manifest声明无效。 - 经典蓝牙(RFCOMM)连接无需定位权限,可移除不必要的定位权限声明,避免权限冲突干扰连接流程。
5. 避免复用旧Socket实例
原代码中Lazy初始化的Socket会在连接失败后复用旧实例,导致后续连接失败。需每次连接都创建新的Socket:
class ConnectThread(private val device: BluetoothDevice,private val bluetoothAdapter: BluetoothAdapter?) : Thread() { private val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") private var mmSocket: BluetoothSocket? = null override fun run() { bluetoothAdapter?.cancelDiscovery() // 每次连接创建新Socket mmSocket = device.createInsecureRfcommSocketToServiceRecord(uuid) mmSocket?.use { socket -> try { socket.connect() // 连接成功逻辑 } catch (e: IOException) { Log.e("TAG", "Connection failed", e) } } } fun cancel() { try { mmSocket?.close() } catch (e: IOException) { Log.e("TAG", "Could not close the client socket", e) } } }
内容的提问来源于stack exchange,提问作者Alexandr
相关产品推荐
相关产品推荐

