在Android中检测BLE设备名称更改

3
我们正在开发一个通过设备名称输出数据的BLE设备。该设备的功能正常,可以使用像nRF Connect这样的应用程序正确更改名称。然而,在我们自己的Android应用程序中进行相同操作很困难。我们可以正常检测到这些设备,但它们几乎无法超越它们最初获得的名称。
我开始使用的代码在onResume()中启动了一个循环,使用BluetoothLeScanner和startScan()函数进行扫描。
public void BLEScan(final boolean enable){

    final int SCAN_PERIOD = 12000;
    final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

    if (enable) {
        Log.d(TAG, "Starting Scan");
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(false);
                invalidateOptionsMenu();
            }
        }, SCAN_PERIOD);
        
        bluetoothLeScanner.flushPendingScanResults(mLeScanCallback);
        bluetoothLeScanner.startScan(mLeScanCallback);
    } else {
        Log.d(TAG, "Stopping Scan");
        bluetoothLeScanner.flushPendingScanResults(mLeScanCallback);
        bluetoothLeScanner.stopScan(mLeScanCallback);
        
        mHandler.removeCallbacksAndMessages(null);

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(true);
            }
        }, SCAN_PERIOD);
    }
}

在mLeScanCallback中检测基于设备的过滤器。基本上,如果以前没有发现该设备,则将其添加到信标列表中。但是,如果以前有见过它,则使用信标的seen()函数更新值,并将信标名称视为已见过的位置,因为信息将从那里获取。在这两种情况下,它都会更新其适配器以填充新信息。

private ScanCallback mLeScanCallback =
    new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            final ScanResult res2 = result;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    BluetoothDevice device = res2.getDevice();
                    String address = device.getAddress();
                    
                    if (device.getName() != null){
                        if (device.getName().contains("ABT:")){
                            if (!mLeBeacons.containsKey(address)){
                                BleBeacon beacon = new BleBeacon(device.getName(), address);
                                bleList.add(beacon);
                                adapter.notifyDataSetChanged();

                                mLeBeacons.put(device.getAddress(), beacon);
                            } else {
                                for (int x = 0; x < bleList.size(); x++){
                                    if (device.getAddress().equals(bleList.get(x).getMAC())){
                                        bleList.get(x).seen(device.getName());
                                        adapter.notifyDataSetChanged();
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }
    };

然而,即使更新了名称,mLeScanCallback仍然只会返回原始名称。在这里进行一些搜索后,我发现要使用fetchUuidsWithSdp()函数以及像ACTION_FOUND、ACTION_UUID、ACTION_DISCOVERY_FINISHED和ACTION_NAME_CHANGED这样的意图才能正确地查看名称更改。因此,我将fetchUuidsWithSdp()添加到了mLeScanCallback中。然而,尽管这会触发Intents,但名称仍然没有更新。我尝试在实际意图中调用fetchUuidsWithSdp(),但这也没有帮助。奇怪的是,如果我关闭手机屏幕或足够远离BLE设备,ACTION_NAME_CHANGED偶尔会触发。但是,onPause()所做的只是调用super.onPause()BLEScan(false)。而且,由于这些都是我已经在循环中完成的事情,我不确定如何在代码处于唤醒状态时将其引入到我的代码中。经过更多搜索,我发现要使用fetchUuidsWithSdq()函数,您需要使用BluetoothAdapter的startDiscovery()函数。因此,我将BLEScan更改为使用它,并完全删除了mLeScanCallback。
public void BLEScan(final boolean enable){
    final int SCAN_PERIOD = 12000;
    if (enable) {
        Log.d(TAG, "Starting Scan");
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(false);
                invalidateOptionsMenu();
            }
        }, SCAN_PERIOD);

        if (mBluetoothAdapter.isDiscovering()){
            mBluetoothAdapter.cancelDiscovery();
        }
        mBluetoothAdapter.startDiscovery();
    } else {
        Log.d(TAG, "Stopping Scan");
        mBluetoothAdapter.cancelDiscovery();

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(true);
            }
        }, SCAN_PERIOD);
    }
}

然而,尽管我的意图筛选器中包含它们,但是之前Intents已经被触发了,而现在却没有触发。在进行更多搜索后,我发现有些人说startDiscovery()不能用于Le设备。因此,我搜索了应该使用的替代方法...这让我回到了使用BluetoothLeScanner的startScan。那时,我意识到自己已经陷入了一个死循环,需要帮助。
我检查了整个过程,因为在某个地方我可能错过了什么。 我不知道我应该使用startScan()吗?我没有正确使用startDiscovery()吗?还是应该完全使用其他东西?ACTION_NAME_CHANGED偶尔被触发的事实让我想回到它,但是如何在设备处于唤醒状态时始终使其起作用呢?
2个回答

0

这并不适用于我,因为我没有连接到设备。我只是在扫描它们。我不想每次都连接到设备来刷新名称。我知道nRF Connect不需要这样做,所以这不应该是必要的。 - Seth
1
也许你可以尝试一下其他答案中提供的方法。根据我对蓝牙低功耗的经验,我仍然认为这是一个缓存问题。 - Michael Kotzjan
我同意这是一个缓存问题。但是,在这种设置中,仅为清除缓存而连接设备并不是一个选项,而且在未连接设备之前,我无法调用BluetoothGatt函数。你有没有办法让我在不连接设备的情况下完成这个操作? - Seth

0

虽然我没有在原始代码中找到我的错误(尽管听起来Android在这方面可能有问题),但我找到了一个解决方法。nRF的NFC工具箱提供了源代码,并且扫描具有添加过滤器以及其自己的来自Nordic库的Le扫描仪的批次。现在我的扫描函数看起来像这样:

    public void BLEScan(final boolean enable){

        final BluetoothLeScannerCompat bluetoothLeScanner = BluetoothLeScannerCompat.getScanner();
        final ScanSettings settings = new ScanSettings.Builder()
                .setLegacy(false)
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).setReportDelay(1000).setUseHardwareBatchingIfSupported(false).build();
        final List<ScanFilter> filters = new ArrayList<>();
        ParcelUuid Uuid = new ParcelUuid(UUID.fromString(uuidString));
        filters.add(new ScanFilter.Builder().setServiceUuid(Uuid).build());

        if (enable) {
            Log.d(TAG, "Starting Scan");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    BLEScan(false);
                    invalidateOptionsMenu();
                }
            }, SCAN_PERIOD);

            bluetoothLeScanner.startScan(filters, settings, mLeScanCallback);
        } else {
            BLEService.refreshGatt();
            Log.d(TAG, "Stopping Scan");
            bluetoothLeScanner.stopScan(mLeScanCallback);

            mHandler.removeCallbacksAndMessages(null);

            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    BLEScan(true);
                }
            }, SCAN_PERIOD);
        }
    }

我的回调函数长这样:

    private no.nordicsemi.android.support.v18.scanner.ScanCallback mLeScanCallback =
            new no.nordicsemi.android.support.v18.scanner.ScanCallback() {
                @Override
                public void onBatchScanResults(@NonNull final List<no.nordicsemi.android.support.v18.scanner.ScanResult> results) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            for (final no.nordicsemi.android.support.v18.scanner.ScanResult result : results){
                                BluetoothDevice device = result.getDevice();

                                if (device != null){
                                    String address = device.getAddress();
                                    String name = result.getScanRecord() != null ? result.getScanRecord().getDeviceName() : null;

                                    if (!mLeBeacons.containsKey(address)) {
                                        BleBeacon beacon = new BleBeacon(device.getName(), address);
                                        bleList.add(beacon);
                                        adapter.notifyDataSetChanged();

                                        mLeBeacons.put(device.getAddress(), beacon);
                                    } else {
                                        for (int x = 0; x < bleList.size(); x++){
                                            if (device.getAddress().equals(bleList.get(x).getMAC())){
                                                bleList.get(x).seen(device.getName());
                                                adapter.notifyDataSetChanged();
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    });
                }

好像解决了问题。不过你需要将以下内容添加到你的依赖项中。

implementation 'no.nordicsemi.android.support.v18:scanner:1.4.2'

之后,我的名字 ACTION_NAME_CHANGE intent 正确触发并且数据正在更新。

我不确定是从结果中检索名称的不同方式还是批量扫描。但是,即使 Nordic 也没有使用标准的 Android BLE 库,我猜这是最好的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接