Android与PC蓝牙连接异常:connectionUrl为空问题求助
Android蓝牙服务端无法被BlueCove客户端获取connectionUrl的问题解决
看起来你在蓝牙角色设定和服务发布上搞反了,这是导致PC端拿不到connectionUrl的核心原因!你说Android是服务端,但你的Android代码里其实是在主动作为客户端去连接已配对的设备,根本没有发布蓝牙服务,PC端自然搜不到对应的服务记录,connectionUrl肯定是空的。
我帮你梳理下问题点和修复方案:
核心问题分析
- 角色完全搞反:蓝牙服务端需要创建
BluetoothServerSocket并发布服务,等待客户端连接;而你的Android代码里用ConnectThread主动连接配对设备,这是客户端的行为。 - 服务未发布:Android端没有通过
listenUsingRfcommWithServiceRecord发布带UUID的服务,PC端的BlueCove搜索不到对应的服务条目,所以拿不到连接URL。 - PC端等待逻辑缺失:你的PC端在发起服务搜索后,没有等待
servicesDiscovered回调完成就直接判断connectionUrl,可能回调还没执行就已经输出"无法建立连接"了。
修复步骤
第一步:把Android端改成真正的服务端
替换原来的配对设备遍历代码,改为启动蓝牙服务端线程,等待PC客户端连接:
- 移除
onCreate里的配对设备遍历代码:
// 删掉这段客户端连接逻辑 Set<BluetoothDevice> pairedDevices = bAdapter.getBondedDevices(); if(pairedDevices.size() > 0){ for(BluetoothDevice device : pairedDevices){ deviceName = device.getName(); deviceAddress = device.getAddress(); mConnectThread = new ConnectThread(device); mConnectThread.start(); } }
- 替换为启动服务端线程的代码:
// 启动蓝牙服务端,等待PC连接 new ServerThread().start();
- 添加服务端线程类(放在MainActivity内部):
private class ServerThread extends Thread { private final BluetoothServerSocket mmServerSocket; private final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private ConnectedThread mConnectedThread; public ServerThread() { BluetoothServerSocket tmp = null; try { // 发布服务,第一个参数是自定义服务名称,第二个是和PC端一致的UUID tmp = bAdapter.listenUsingRfcommWithServiceRecord("SensorDataService", myUUID); Log.d(TAG, "蓝牙服务端已启动,等待连接..."); } catch (IOException e) { Log.e(TAG, "创建ServerSocket失败", e); } mmServerSocket = tmp; } public void run() { BluetoothSocket socket = null; // 循环等待客户端连接(这里只处理一个连接,可按需修改) while (true) { try { socket = mmServerSocket.accept(); Log.d(TAG, "PC客户端已连接"); } catch (IOException e) { Log.e(TAG, "连接失败或断开", e); break; } if (socket != null) { // 连接成功后启动数据发送线程 mConnectedThread = new ConnectedThread(socket); mConnectedThread.start(); try { mmServerSocket.close(); // 只处理一个连接,关闭服务器 } catch (IOException e) { Log.e(TAG, "关闭ServerSocket失败", e); } break; } } } public void cancel() { try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "关闭ServerSocket失败", e); } } }
- 修改传感器数据发送的判断逻辑(替换原来的
mConnectThread.socketAccquired()):
比如在onSensorChanged方法里:
if(mConnectedThread != null) { mConnectedThread.write(float2ByteArray(AccX)); mConnectedThread.write(float2ByteArray(AccY)); mConnectedThread.write(float2ByteArray(AccZ)); mConnectedThread.write(float2ByteArray(GyroX)); mConnectedThread.write(float2ByteArray(GyroY)); mConnectedThread.write(float2ByteArray(GyroZ)); mConnectedThread.write((String.valueOf(clicked)).getBytes()); }
第二步:修复PC端的服务搜索逻辑
主要是修复UUID构造方式,添加服务搜索的等待逻辑,确保回调完成后再判断connectionUrl:
修改go方法里的服务搜索部分:
private void go() throws Exception { scanFinished = false; LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, new DiscoveryListener() { @Override public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { try { String name = btDevice.getFriendlyName(false); if (name.matches("Mi.*")) { System.out.format("找到目标设备:%s (%s)\n", name, btDevice.getBluetoothAddress()); bluetoothdevice = btDevice; } } catch (IOException e) { e.printStackTrace(); } } @Override public void inquiryCompleted(int discType) { scanFinished = true; } @Override public void serviceSearchCompleted(int transID, int respCode) {} @Override public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {} }); while (!scanFinished) { Thread.sleep(500); } if (bluetoothdevice == null) { System.out.println("未找到目标手机设备"); return; } // 用字符串解析UUID,避免构造错误 UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); UUID[] searchUuidSet = new UUID[]{uuid}; scanFinished = false; // 重置标志位 LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(null, searchUuidSet, bluetoothdevice, new DiscoveryListener() { @Override public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {} @Override public void inquiryCompleted(int discType) {} @Override public void serviceSearchCompleted(int transID, int respCode) { scanFinished = true; if (respCode != DiscoveryListener.SERVICE_SEARCH_COMPLETED) { System.out.println("服务搜索失败,响应码:" + respCode); } } @Override public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { for (ServiceRecord record : servRecord) { connectionUrl = record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); if (connectionUrl != null) { System.out.println("获取到连接URL:" + connectionUrl); break; } } } }); // 等待服务搜索完成 while (!scanFinished) { Thread.sleep(500); } if(connectionUrl == null) { System.out.println("无法获取连接URL,请检查:1. 手机是否启动蓝牙服务;2. 设备是否已配对;3. UUID是否一致"); return; } StreamConnection streamConnection = (StreamConnection)Connector.open(connectionUrl); System.out.println("连接成功!开始接收传感器数据..."); // 启动数据处理线程 new Thread(new processConnThread(streamConnection)).start(); }
额外注意事项
- Android权限:确保AndroidManifest.xml里添加了必要权限,Android 6+还要动态申请位置权限(蓝牙搜索依赖位置权限):
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- 设备配对:提前在PC和手机上完成蓝牙配对,避免服务搜索失败。
- BlueCove版本:建议使用
bluecove-2.1.1-SNAPSHOT版本,对SPP服务的兼容性更好。 - 线程安全:Android端发送传感器数据时,建议把所有数据打包成一个字节数组再发送,避免多次write导致数据拆分混乱。
内容的提问来源于stack exchange,提问作者j0ker
相关产品推荐
相关产品推荐

