繁体   English   中英

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

[英]Detecting BLE Device name change in Android

我们有一个正在开发的 BLE 设备,它通过设备名称输出数据。 该设备运行正常,可以看到使用 nRF Connect 等应用程序正确更改名称。 但是,在我们自己的 Android 应用程序中,我们很难做到这一点。 我们可以很好地检测到这些设备,但它们几乎永远不会超过最初的名称。

我开始使用的代码有一个在 onResume() 中启动的循环,该循环使用 BluetoothLeScanner 和 startScan() function 进行扫描。

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 中检测到。 基本上,如果它以前没有见过设备,它会将其添加到信标列表中。 但是,如果它以前见过它,它会使用 Beacon 的 seen() function 更新值,并使用 Beacon 的名称作为信息的来源。 在这两种情况下,它都会更新其适配器以填充新信息。

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 也只会返回原始名称。

经过这里的一些搜索后,我一直在寻找的是使用 function fetchUuidsWithSdp() 和 ACTION_FOUND、ACTION_UUID、ACTION_DISCOVERY_FINISHED 和 ACTION_NAME_CHANGED 等意图来正确查看名称更改。 因此,我将 fetchUuidsWithSdp() 添加到 mLeScanCallback。 然而,虽然这会触发 Intent,但名称仍然没有更新。 我尝试在实际意图中调用 fetchUuidsWithSdp() ,但这也无济于事。 奇怪的是,如果我关闭手机屏幕或离 BLE 设备足够远,ACTION_NAME_CHANGED 偶尔会触发。 但是,所有 onPause() 所做的只是调用super.onPause()BLEScan(false) 而且,由于这些是我在循环中已经在做的事情,所以我不确定如何在它醒着时将它带入我的代码中。

经过更多搜索,我发现,要使用 fetchUuidsWithSdq() function,您需要使用 BluetoothAdapter 的 startDiscovery() function。 所以,我改变了 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);
    }
}

然而,当 Intent 之前触发时,它们现在没有触发,即使它们在我的意图过滤器中。 在进行了更多搜索之后,我发现有人说 startDiscovery() 不适用于 Le 设备。 所以,我搜索了你应该使用的东西......这让我一直回到使用 BluetoothLeScanner 的 startScan。 那时,我意识到我绕了一个圈子,需要帮助。

我回顾了整个过程,因为在某些地方,我错过了一些东西。 我只是不知道它在哪里。 我应该使用 startScan() 吗? 我没有正确使用 startDiscovery() 吗? 还是我应该完全使用其他东西? ACTION_NAME_CHANGED 偶尔触发的事实让我想 go 回到那个状态,但是当设备处于唤醒状态时如何让它始终工作?

我认为您可能只是在 android 上遇到缓存问题。 在此处查看此答案以获得可能的解决方案: https://stackoverflow.com/a/50745997/7473793

虽然我没有发现我在原始代码中做错了什么(尽管听起来 Android 在这方面可能只是被破坏了),但我确实找到了一种解决方法。 nRF 的 NFC 工具箱有它的源代码可用,并通过添加的过滤器以及它自己的来自北欧图书馆的 Le 扫描仪进行批量扫描。 这是我的扫描 function 现在的样子:

    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 意图正确触发并且数据正在更新。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM