You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android与PC蓝牙连接异常:connectionUrl为空问题求助

Android蓝牙服务端无法被BlueCove客户端获取connectionUrl的问题解决

看起来你在蓝牙角色设定和服务发布上搞反了,这是导致PC端拿不到connectionUrl的核心原因!你说Android是服务端,但你的Android代码里其实是在主动作为客户端去连接已配对的设备,根本没有发布蓝牙服务,PC端自然搜不到对应的服务记录,connectionUrl肯定是空的。

我帮你梳理下问题点和修复方案:

核心问题分析

  1. 角色完全搞反:蓝牙服务端需要创建BluetoothServerSocket并发布服务,等待客户端连接;而你的Android代码里用ConnectThread主动连接配对设备,这是客户端的行为。
  2. 服务未发布:Android端没有通过listenUsingRfcommWithServiceRecord发布带UUID的服务,PC端的BlueCove搜索不到对应的服务条目,所以拿不到连接URL。
  3. PC端等待逻辑缺失:你的PC端在发起服务搜索后,没有等待servicesDiscovered回调完成就直接判断connectionUrl,可能回调还没执行就已经输出"无法建立连接"了。

修复步骤

第一步:把Android端改成真正的服务端

替换原来的配对设备遍历代码,改为启动蓝牙服务端线程,等待PC客户端连接:

  1. 移除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();
    }
}
  1. 替换为启动服务端线程的代码:
// 启动蓝牙服务端,等待PC连接
new ServerThread().start();
  1. 添加服务端线程类(放在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);
        }
    }
}
  1. 修改传感器数据发送的判断逻辑(替换原来的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();
}

额外注意事项

  1. 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" />
  1. 设备配对:提前在PC和手机上完成蓝牙配对,避免服务搜索失败。
  2. BlueCove版本:建议使用bluecove-2.1.1-SNAPSHOT版本,对SPP服务的兼容性更好。
  3. 线程安全:Android端发送传感器数据时,建议把所有数据打包成一个字节数组再发送,避免多次write导致数据拆分混乱。

内容的提问来源于stack exchange,提问作者j0ker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:47:35