Android에서 BLE 장치 이름 변경 감지
우리는 장치의 이름을 통해 데이터를 출력하는 BLE 장치가 있습니다. 장치가 제대로 작동하고 nRF Connect와 같은 앱을 사용하여 이름이 제대로 변경되는 것을 볼 수 있습니다. 그러나 우리는 우리 자신의 안드로이드 앱에서 똑같은 일을하는 데 어려움을 겪고 있습니다. 우리는 장치를 잘 감지 할 수 있지만, 주어진 원래 이름을 지나치는 일은 거의 없습니다.
내가 시작한 코드에는 BluetoothLeScanner 및 startScan () 함수를 사용하여 스캔하는 onResume ()에서 시작되는 루프가 있습니다.
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의 이름으로 Beacon의 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와 같은 인 텐트를 사용하여 이름이 제대로 변경되었는지 확인했습니다. 그래서 mLeScanCallback에 fetchUuidsWithSdp ()를 추가했습니다. 그러나 이것이 인 텐트를 트리거하는 동안 이름은 여전히 업데이트되지 않았습니다. 실제 의도에서 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);
}
}
그러나 인 텐트가 이전에 실행되는 동안 내 인 텐트 필터에 있었음에도 불구하고 지금은 그렇지 않았습니다. 더 많은 검색을 한 후 startDiscovery ()가 Le 장치에서 작동하지 않는다고 말하는 사람들을 발견했습니다. 그래서 대신에 사용해야하는 것을 검색했습니다. 이로 인해 BluetoothLeScanner를 사용하여 스캔을 시작할 수있게되었습니다. 그 시점에서 나는 원을 그리며 도움이 필요하다는 것을 깨달았습니다.
선을 따라 어딘가에서 무언가를 놓 쳤기 때문에 전체 프로세스를 검토했습니다. 그게 어디 였는지 모르겠어요. startScan ()을 사용해야합니까? startDiscovery ()를 제대로 사용하고 있지 않습니까? 아니면 내가 완전히 사용해야 할 다른 것이 있습니까? ACTION_NAME_CHANGED가 가끔 실행된다는 사실로 인해 다시 돌아가고 싶지만 기기가 깨어있을 때 항상 작동하게하려면 어떻게해야하나요?
답변
나는 당신이 단순히 안드로이드에서 캐싱에 문제가 있다고 생각합니다. 가능한 해결책은 여기에서이 답변을 참조하십시오.https://stackoverflow.com/a/50745997/7473793
원래 코드에서 내가 뭘 잘못하고 있는지 찾지 못했지만 (이 점에서 Android가 고장난 것처럼 들리지만) 해결 방법을 찾았습니다. nRF의 NFC Toolbox는 소스 코드를 사용할 수 있으며 필터가 추가 된 일괄 스캔과 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 인 텐트가 제대로 트리거되고 데이터가 업데이트됩니다.
결과에서 이름을 검색하는 다른 방법인지 또는 일괄 스캔인지 확실하지 않습니다. 그러나 Nordic조차도 표준 Android BLE 라이브러리를 사용하지 않는다면 이것이 최선의 방법이라고 생각합니다.