在 Android 中检测 BLE 设备名称更改
Detecting BLE Device name change in Android
我们有一个正在使用的 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 中检测到基于设备进行过滤。基本上,如果它之前没有看到设备,它会将其添加到信标列表中。但是,如果它之前看到过它,它会使用 Beacon 的 seen() 函数和 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 也只会 return 原来的名称。
在这里搜索一番后,我不断发现的是使用函数 fetchUuidsWithSdp() 和 ACTION_FOUND、ACTION_UUID、ACTION_DISCOVERY_FINISHED 和 [=35 等意图=] 以正确查看名称更改。因此,我将 fetchUuidsWithSdp() 添加到 mLeScanCallback。然而,虽然这会触发 Intents,但名称仍未更新。我尝试在实际意图中调用 fetchUuidsWithSdp() ,但这也没有帮助。奇怪的是,如果我关闭 phone 的屏幕或离 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);
}
}
然而,尽管 Intent 在我的 Intent 过滤器中,但之前会触发,但现在不会。在做了更多搜索之后,我发现有人说 startDiscovery() 不适用于 Le 设备。所以,我搜索了你应该使用的东西......这让我一路回到 startScan with BluetoothLeScanner。那时,我意识到我绕了一个圈,需要帮助。
我检查了整个过程,因为在某些地方,我遗漏了一些东西。我只是不知道它在哪里。我应该使用 startScan() 吗?我没有正确使用 startDiscovery() 吗?还是我应该完全使用其他东西? ACTION_NAME_CHANGED 偶尔触发的事实让我想回到那个,但我如何让它在设备处于唤醒状态时始终工作?
我认为您可能只是在 android 上遇到缓存问题。请在此处查看此答案以获得可能的解决方案:
虽然我没有发现我在原始代码中做错了什么(尽管听起来 Android 可能只是在这方面被破坏),但我确实找到了解决方法。 nRF 的 NFC 工具箱具有可用的源代码,并使用添加的过滤器进行批量扫描,以及它自己的来自北欧图书馆的 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 意图正确触发并且数据正在更新。
我不确定这是从结果中检索名称的不同方式还是批量扫描。但是,如果连 Nordic 都不使用标准 Android BLE 库,我猜这是最好的方法。
我们有一个正在使用的 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 中检测到基于设备进行过滤。基本上,如果它之前没有看到设备,它会将其添加到信标列表中。但是,如果它之前看到过它,它会使用 Beacon 的 seen() 函数和 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 也只会 return 原来的名称。
在这里搜索一番后,我不断发现的是使用函数 fetchUuidsWithSdp() 和 ACTION_FOUND、ACTION_UUID、ACTION_DISCOVERY_FINISHED 和 [=35 等意图=] 以正确查看名称更改。因此,我将 fetchUuidsWithSdp() 添加到 mLeScanCallback。然而,虽然这会触发 Intents,但名称仍未更新。我尝试在实际意图中调用 fetchUuidsWithSdp() ,但这也没有帮助。奇怪的是,如果我关闭 phone 的屏幕或离 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);
}
}
然而,尽管 Intent 在我的 Intent 过滤器中,但之前会触发,但现在不会。在做了更多搜索之后,我发现有人说 startDiscovery() 不适用于 Le 设备。所以,我搜索了你应该使用的东西......这让我一路回到 startScan with BluetoothLeScanner。那时,我意识到我绕了一个圈,需要帮助。
我检查了整个过程,因为在某些地方,我遗漏了一些东西。我只是不知道它在哪里。我应该使用 startScan() 吗?我没有正确使用 startDiscovery() 吗?还是我应该完全使用其他东西? ACTION_NAME_CHANGED 偶尔触发的事实让我想回到那个,但我如何让它在设备处于唤醒状态时始终工作?
我认为您可能只是在 android 上遇到缓存问题。请在此处查看此答案以获得可能的解决方案:
虽然我没有发现我在原始代码中做错了什么(尽管听起来 Android 可能只是在这方面被破坏),但我确实找到了解决方法。 nRF 的 NFC 工具箱具有可用的源代码,并使用添加的过滤器进行批量扫描,以及它自己的来自北欧图书馆的 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 意图正确触发并且数据正在更新。
我不确定这是从结果中检索名称的不同方式还是批量扫描。但是,如果连 Nordic 都不使用标准 Android BLE 库,我猜这是最好的方法。