Python基于pyjnius开发安卓蓝牙客户端连接报socket读取失败如何解决
解决方案
问题根因
你遇到的jnius.jnius.JavaException: JVM exception occurred: read failed, socket might closed or timeout, read ret: -1是Android蓝牙框架的经典兼容性问题,官方提供的createRfcommSocketToServiceRecord方法在部分设备、Android版本上无法正确匹配RFCOMM通道,需要通过反射调用系统隐藏的方法直接指定端口号连接。
完整可运行代码
from jnius import autoclass, JavaException server_mac_address = '94:08:53:52:B5:98' uuid = "00001101-0000-1000-8000-00805F9B34FB" BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter') BluetoothDevice = autoclass('android.bluetooth.BluetoothDevice') BluetoothSocket = autoclass('android.bluetooth.BluetoothSocket') UUID = autoclass('java.util.UUID') Integer = autoclass('java.lang.Integer') def get_socket_stream(): adapter = BluetoothAdapter.getDefaultAdapter() device = adapter.getRemoteDevice(server_mac_address) socket = None # 先尝试标准方法,失败后走反射 fallback 方案 try: socket = device.createRfcommSocketToServiceRecord(UUID.fromString(uuid)) adapter.cancelDiscovery() socket.connect() except JavaException: # 反射调用隐藏方法,指定RFCOMM通道1,和pybluez默认rfcomm-server端口对应 create_rfcomm_method = device.getClass().getMethod("createRfcommSocket", [Integer.TYPE]) socket = create_rfcomm_method.invoke(device, 1) adapter.cancelDiscovery() socket.connect() recv_stream = socket.getInputStream() send_stream = socket.getOutputStream() return recv_stream, send_stream if __name__ == '__main__': recv_stream, send_stream = get_socket_stream() # 注意转换为bytes传输,否则会抛类型错误 send_stream.write('hello\n'.encode('utf-8')) send_stream.flush()
必做检查项
- 确认你运行的pybluez服务端用的是默认端口1:如果修改过rfcomm-server.py的port参数,将代码里
create_rfcomm_method.invoke(device, 1)的1替换为你实际使用的端口号 - 确认APP已拿到全部蓝牙权限:Android 12及以上需要申请
BLUETOOTH_SCAN、BLUETOOTH_CONNECT、ACCESS_FINE_LOCATION权限,Android 11及以下需要BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_COARSE_LOCATION权限,未授权会直接导致连接失败 - 确认连接时蓝牙没有在执行其他扫描任务:扫描会占用蓝牙硬件带宽,导致连接超时,代码里的
cancelDiscovery()必须保留 - 确认设备配对状态正常:如果配对失效,先删除两端的配对记录,重新配对后再运行代码
内容的提问来源于stack exchange,提问作者Kirill
相关产品推荐
相关产品推荐

