Erkennen der Änderung des BLE-Gerätenamens in Android
Wir haben ein BLE-Gerät, an dem wir arbeiten und das Daten über den Gerätenamen ausgibt. Das Gerät funktioniert ordnungsgemäß und kann mithilfe einer App wie nRF Connect beim Ändern von Namen angezeigt werden. Es fällt uns jedoch schwer, dasselbe in unserer eigenen Android-App zu tun. Wir können die Geräte gut erkennen, aber sie werden fast nie über die ursprünglichen Namen hinausgehen, die ihnen gegeben wurden.
Der Code, mit dem ich begonnen habe, hat eine Schleife, die in onResume () gestartet wird und mit einem BluetoothLeScanner und der Funktion startScan () scannt.
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);
}
}
In mLeScanCallback wird erkannt, dass basierend auf Geräten gefiltert wird. Wenn ein Gerät noch nicht gesehen wurde, wird es einer Liste von Beacons hinzugefügt. Wenn es es jedoch schon einmal gesehen hat, aktualisiert es die Werte mit der Funktion saw () des Beacons mit dem Namen des Beacons, da die Informationen von dort stammen. In beiden Fällen wird der Adapter aktualisiert, um die neuen Informationen aufzufüllen.
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();
}
}
}
}
}
}
});
}
};
Auch nach der Aktualisierung des Namens gibt mLeScanCallback immer nur den ursprünglichen Namen zurück.
Nach einigem Suchen hier war das, was ich immer wieder fand, die Funktion fetchUuidsWithSdp () und Absichten wie ACTION_FOUND, ACTION_UUID, ACTION_DISCOVERY_FINISHED und ACTION_NAME_CHANGED zu verwenden, um zu sehen, wie sich der Name richtig ändert. Also habe ich fetchUuidsWithSdp () zu mLeScanCallback hinzugefügt. Obwohl dies die Absichten auslösen würde, wurde der Name immer noch nicht aktualisiert. Ich habe versucht, fetchUuidsWithSdp () in der eigentlichen Absicht aufzurufen, aber das hat auch nicht geholfen. Seltsamerweise wurde ACTION_NAME_CHANGED gelegentlich ausgelöst, wenn ich den Bildschirm meines Telefons ausschaltete oder weit genug vom BLE-Gerät entfernt war. Aber alles, was onPause () tut, ist aufzurufen super.onPause()
und BLEScan(false)
. Und da dies Dinge waren, die ich bereits in meiner Schleife tat, war ich mir nicht sicher, wie ich das in meinen Code bringen sollte, während er wach war.
Nach weiteren Suchen stellte ich fest, dass Sie zur Verwendung der Funktion fetchUuidsWithSdq () die Funktion startDiscovery () Ihres BluetoothAdapters verwenden müssen. Also habe ich BLEScan geändert, um es zu verwenden, und mLeScanCallback vollständig ausgeschnitten.
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);
}
}
Während die Absichten vorher feuerten, waren sie es jetzt nicht, obwohl sie in meinem Absichtsfilter waren. Nachdem ich weiter gesucht hatte, fand ich einige Leute, die sagten, dass startDiscovery () mit Le-Geräten nicht funktioniert. Also habe ich nach dem gesucht, was Sie stattdessen verwenden sollen ... was mich den ganzen Weg zurück zu startScan mit BluetoothLeScanner gebracht hat. Zu diesem Zeitpunkt war mir klar geworden, dass ich mich im Kreis bewegt hatte und Hilfe brauchte.
Ich habe meinen gesamten Prozess durchgesehen, weil ich irgendwo etwas verpasst habe. Ich weiß nur nicht, wo es war. Sollte ich startScan () verwenden? Benutze ich startDiscovery () nicht richtig? Oder gibt es noch etwas, das ich ganz verwenden sollte? Die Tatsache, dass ACTION_NAME_CHANGED gelegentlich ausgelöst wurde, veranlasst mich, darauf zurückzukommen. Aber wie kann ich es jederzeit zum Laufen bringen, wenn das Gerät wach ist?
Antworten
Ich denke, Sie könnten einfach ein Problem mit dem Caching auf Android haben. In dieser Antwort finden Sie eine mögliche Lösung:https://stackoverflow.com/a/50745997/7473793
Obwohl ich in meinem ursprünglichen Code nicht gefunden habe, was ich falsch gemacht habe (obwohl es so klingt, als ob Android in dieser Hinsicht möglicherweise kaputt ist), habe ich eine Problemumgehung gefunden. In der NFC Toolbox von nRF ist der Quellcode verfügbar und es werden Stapel mit hinzugefügten Filtern sowie ein eigener Le-Scanner aus einer nordischen Bibliothek gescannt. So sieht meine Scanfunktion jetzt aus:
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);
}
}
Und mein Rückruf sieht so aus:
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();
}
}
}
}
}
}
});
}
Das schien den Trick zu tun. Sie müssen Ihren Abhängigkeiten jedoch Folgendes hinzufügen.
implementation 'no.nordicsemi.android.support.v18:scanner:1.4.2'
Danach wird die Absicht meines Namens ACTION_NAME_CHANGE ordnungsgemäß ausgelöst und die Daten werden aktualisiert.
Ich bin mir nicht sicher, ob es die andere Art ist, den Namen aus dem Ergebnis abzurufen, oder ob es sich um ein Batch-Scannen handelt. Aber wenn selbst Nordic nicht die Standard-Android-BLE-Bibliothek verwendet, ist dies vermutlich der beste Weg.