Android:蓝牙低功耗扫描程序接收空数据

use*_*180 16 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);
}
Run Code Online (Sandbox Code Playgroud)

它成功地开始了.

这是扫描仪

 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);
    }
};
Run Code Online (Sandbox Code Playgroud)

在回调中,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]
Run Code Online (Sandbox Code Playgroud)

不幸的是,uuid匹配的结果

  byte[] data = scanRecord.getServiceData(uuid) 
Run Code Online (Sandbox Code Playgroud)

null.我注意到toString输出的广告数据字符为"123456"的ASCII码,即49,50,51,52,53,54

 mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}
Run Code Online (Sandbox Code Playgroud)

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

编辑:清单具有蓝牙,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-blocks)

   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()));
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

其实就行了

 map.containsKey(uuid)
Run Code Online (Sandbox Code Playgroud)

返回false,因为数据映射不使用广告商的uuid.

我不得不导航地图以找到值(第二个if-block),但我没有办法知道这是否是我感兴趣的值.无论哪种方式,如果系统放置我都无法得到值在接收器应用程序上运行扫描程序代码时我无法知道的另一个键.

我怎样才能在接收器上处理这个问题?我想使用数据字段,但获取它们的字符串键不是先验已知的并且由系统决定.

小智 0

我发现了错误。更改这一行:

.addServiceData(pUuid, "123456".getBytes(Charset.forName("UTF-8")))
Run Code Online (Sandbox Code Playgroud)

到:

.addServiceData(pUuid, "123456".getBytes()
Run Code Online (Sandbox Code Playgroud)

就是这样。我使用了与您相同的示例代码。

在这里找到一个更好的例子: https:
//github.com/googlesamples/android-BluetoothAdvertisements