Обнаружение изменения имени устройства BLE в 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 visible () с именем 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 вернет только исходное имя.
После некоторого поиска здесь я обнаружил, что я использовал функцию fetchUuidsWithSdp () и такие намерения, как ACTION_FOUND, ACTION_UUID, ACTION_DISCOVERY_FINISHED и ACTION_NAME_CHANGED, чтобы правильно увидеть изменение имени. Итак, я добавил fetchUuidsWithSdp () в mLeScanCallback. Однако, хотя это вызвало бы намерения, имя все еще не обновлялось. Я пробовал вызвать fetchUuidsWithSdp () в реальных намерениях, но это тоже не помогло. Как ни странно, ACTION_NAME_CHANGED иногда срабатывает, если я выключаю экран телефона или отхожу достаточно далеко от устройства BLE. Но все, что делает onPause (), - это вызов super.onPause()и BLEScan(false). И поскольку это были вещи, которые я уже делал в своем цикле, я не был уверен, как внести это в свой код, пока он не спал.
После дополнительных поисков я обнаружил, что для использования функции fetchUuidsWithSdq () вам необходимо использовать функцию startDiscovery () вашего BluetoothAdapter. Итак, я изменил 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);
}
}
Однако если раньше намерения срабатывали, то теперь их не было, хотя они были в моем фильтре намерений. После еще нескольких поисков я обнаружил, что некоторые люди говорят, что startDiscovery () не работает с устройствами Le. Итак, я искал то, что вы должны использовать вместо этого ... что вернуло меня к startScan с BluetoothLeScanner. В этот момент я понял, что пошел по кругу и нуждался в помощи.
Я просмотрел весь свой процесс, потому что где-то по ходу дела я что-то упустил. Я просто не знаю, где это было. Стоит ли использовать startScan ()? Я неправильно использую startDiscovery ()? Или есть что-то еще, что я должен полностью использовать? Тот факт, что ACTION_NAME_CHANGED срабатывает, иногда заставляет меня вернуться к этому, но как мне заставить его работать все время, когда устройство не спит?
Ответы
Думаю у вас может просто проблема с кешированием на андроиде. См. Этот ответ здесь для возможного решения:https://stackoverflow.com/a/50745997/7473793
Хотя я не обнаружил, что я делал неправильно в исходном коде (хотя похоже, что Android может просто сломаться в этом отношении), я нашел обходной путь. NFC Toolbox от nRF имеет доступный исходный код и сканирует партиями с добавленными фильтрами, а также имеет собственный сканер 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, я думаю, это лучший способ.