java - Android:低功耗蓝牙扫描器接收到空数据

标签 java android bluetooth bluetooth-lowenergy btle

这是广告商(注意 data 作为 AdvertiseData 类型传递)

  private void advertise() {
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    BluetoothLeAdvertiser advertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
    AdvertiseSettings settings = new AdvertiseSettings.Builder()
            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
            .setConnectable(false)
            .build();
    ParcelUuid pUuid = new ParcelUuid(UUID.fromString("cf2c82b6-6a06-403d-b7e6-13934e602664"));
    AdvertiseData data = new AdvertiseData.Builder()
            //.setIncludeDeviceName(true)
            .addServiceUuid(pUuid)
            .addServiceData(pUuid, "123456".getBytes(Charset.forName("UTF-8")))
            .build();
    AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            Log.i(tag, "Advertising onStartSuccess");
            super.onStartSuccess(settingsInEffect);
        }

        @Override
        public void onStartFailure(int errorCode) {
            Log.e(tag, "Advertising onStartFailure: " + errorCode);
            super.onStartFailure(errorCode);
        }
    };
    advertiser.startAdvertising(settings, data, advertiseCallback);
}

启动成功

这是扫描仪

 private void discover() {
    ScanSettings settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
            .build();
    mBluetoothLeScanner.startScan(null, settings, mScanCallback);
}

private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        Log.i(tag, "Discovery onScanResult");
        if (result == null) {
            Log.i(tag, "no result");
            return;
        }
        ScanRecord scanRecord = result.getScanRecord();
        List<ParcelUuid> list = scanRecord != null ? scanRecord.getServiceUuids() : null;
        if (list != null) {
            Log.i(tag, scanRecord.toString());
            for (ParcelUuid uuid : list) {
                byte[] data = scanRecord.getServiceData(uuid);
            }
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
        Log.i(tag, "Discovery onBatchScanResults");
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.e(tag, "Discovery onScanFailed: " + errorCode);
    }
};

在回调 onScarnResult 中,我记录了生成此输出的扫描记录 toString()

 ScanRecord [mAdvertiseFlags=2, 
         mServiceUuids=[cf2c82b6-6a06-403d-b7e6-13934e602664],
         mManufacturerSpecificData={}, 
         mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}, 
         mTxPowerLevel=-2147483648, mDeviceName=null]

uuid匹配,不幸的是

的结果
  byte[] data = scanRecord.getServiceData(uuid) 

null。 我注意到 toString 输出具有广告数据字符“123456”的 ASCII 代码,即 49,50,51,52,53,54

 mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}

我想收到正确的广告数据,我做错了什么吗?

编辑: list 具有蓝牙、bt 管理和位置权限。第三个在 Android 6 中在运行时发起请求

编辑:通过打印整个 scanRecord 你得到这个输出

ScanRecord [mAdvertiseFlags=-1, mServiceUuids=[cf2c82b6-6a06-403d-b7e6-13934e602664], mManufacturerSpecificData={}, mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}, mTxPowerLevel=-2147483648, mDeviceName=null]

基本上你不能使用广告商决定的uuid,它在mServiceUuids数组中,因为关联到mServiceData的键是另一个。所以我以这种方式更改了代码,以导航数据映射并获取值(请参阅两个 if block )

   public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
        for (ScanResult result : results) {
            ScanRecord scanRecord = result.getScanRecord();
            List<ParcelUuid> uuids = scanRecord.getServiceUuids();
            Map<ParcelUuid, byte[]> map = scanRecord.getServiceData();
            if (uuids != null) {
                for (ParcelUuid uuid : uuids) {
                    byte[] data = scanRecord.getServiceData(uuid);
                    Log.i(tag, uuid + " -> " + data + " contain " + map.containsKey(uuid));
                }
            }

            if (map != null) {
                Set<Map.Entry<ParcelUuid, byte[]>> set = map.entrySet();
                Iterator<Map.Entry<ParcelUuid, byte[]>> iterator = set.iterator();
                while (iterator.hasNext()) {
                    Log.i(tag, new String(iterator.next().getValue()));
                }
            }
        }
    }

其实行

 map.containsKey(uuid)

返回false,因为广告商的uuid没有被数据映射使用。

我必须在 map 上导航才能找到该值(第二个 if block ),但我没有任何方法可以知道这是否是我感兴趣的值。无论哪种方式,我都无法获得该值 if在接收器应用程序上运行扫描仪代码时,系统放置了另一个我不知道的 key 。

我该如何处理接收器上的这个问题?我想使用数据字段,但获取它们的字符串键不是先验的,是由系统决定的。

最佳答案

我知道这是一个旧线程,但由于我遇到了同样的问题并找到了解决方案...

与广告商中的 .addServiceUuid().addServiceData() 一起使用的 UUID 是不同的对象。 第一个标识服务,是一个 128 位的 UUID。第二个标识该服务中的 serviceData,预计为 16 位 UUID。

这就是扫描器接收的原因

0000**82b6**-0000-1000-8000-00805f9b34fb

请注意,16 位 0x82b6 与传递给 .addServiceData 的 UUID 相同:

cf2c**82b6**-6a06-403d-b7e6-13934e602664

通过左移 96 位并添加蓝牙常量 UUID,将 16 位 UUID 转换为 128 位。

解决方案就是使用这种形式的 UUID [0000xxxx-0000-1000-8000-00805f9b34fb ] 来识别广告商和扫描仪中的服务数据。您可以保留您原来的 128 位 UUID 来标识该服务。

关于java - Android:低功耗蓝牙扫描器接收到空数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38241691/

相关文章:

java - 如何在 Java 中按部分加载序列化对象

java - 用于将 PDF 文件转换为位图的最可靠的开源 Java 库

java - 在 Java 中将 ArrayList<ArrayList<E>> 转换为 E[][]

java - 在类中创建 HashMap ?

没有 TwitterLoginButton 的 Android Fabric TwitterCore 登录

android - 如何在 React Native(iOS 和 Android)中获取设备的当前国家/地区?

android - 在牛轧糖中录制蓝牙耳机音频

bluetooth - 苹果心率监测实例及蓝牙心率测量特性字节序

Android - 如何长时间计算时间

Kit Kat 中的 Android BT 堆栈