iOS 장치에서 생성 된 BLE 장치를 검색하고 있습니다. 그런 다음 특정 서비스에 연결하고 특정 특성을 읽습니다. GATT 서비스가있는 iOS 앱이 포 그라운드에있을 때 완벽하게 작동합니다. 하지만 iOS 서버 앱을 숨기면 Android 클라이언트가 BLE GATT 기기를 감지하지 않습니다.배경에있는 BLE 장치를 검색 할 때 RxBleClient를 사용할 때의 문제
public static ScanFilter[] getFilters(UUID serviceUuid) {
...
filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuid)).build());
return filters.toArray(new ScanFilter[filters.size()]);
}
public static ScanSettings getScanSettings() {
return new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build();
}
BLE Scanner app 성공적으로 숨겨 본다 GATT 서버
업데이트 다음 필터 코드 부분
public final class ScannerUtil {
public static final List<UUID> BEACON_UUUIDs = Arrays.asList(
...........
UUID.fromString("a8427a96-70bd-4a7e-9008-6e5c3d445a2b"));
public static ScanSettings getScanSettings() {
return new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_BALANCED) // change if needed
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build();
}
public static ScanFilter[] getFilters(UUID serviceUuid) {
List<ScanFilter> filters = Stream.of(BEACON_UUUIDs)
.map(iBeaconScanFilter::setScanFilter)
.collect(toList());
filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuid)).build());
return filters.toArray(new ScanFilter[filters.size()]);
}
}
전체 스캐너 클래스 코드는 다음입니다 :
public class BLEGlobalScanner {
private final ScannerConfiguration configuration;
private final Context context;
private final RxBleClient rxBleClient;
private final Map<String, String> devicesMap = new HashMap<>();
private final Map<String, DeviceApoloBeacon> beaconsMap = new HashMap<>();
private final ScanFilter[] scanFilter;
public BLEGlobalScanner(ScannerConfiguration configuration, Context context) {
this.configuration = configuration;
this.context = context;
this.rxBleClient = RxBleClient.create(context);
this.scanFilter = getFilters(configuration.beacons(), configuration.gattServer().server());
}
public Observable<BluetoothDeviceApolo> start() {
return bluetoothEnableObservable(context).switchMap(aBoolean -> startScanner())
.filter(Optional::isPresent)
.map(Optional::get);
}
private Observable<Optional<BluetoothDeviceApolo>> startScanner() {
return rxBleClient.scanBleDevices(getScanSettings(), scanFilter)
.buffer(2, TimeUnit.SECONDS)
.flatMap(rxBleDevices -> Observable.from(rxBleDevices)
.distinct(scanResult -> scanResult.getBleDevice().getMacAddress())
.concatMap(this::handleDevices)
.map(Optional::of))
.observeOn(mainThread())
.onErrorResumeNext(throwable -> {
Timber.e(throwable, "startScanner");
return Observable.just(Optional.empty());
})
.onExceptionResumeNext(Observable.just(Optional.empty()))
.retry();
}
private Observable<BluetoothDeviceApolo> handleDevices(ScanResult scanResult) {
if (beaconsMap.containsKey(scanResult.getBleDevice().getMacAddress())) {
return Observable.fromCallable(() -> beaconsMap.get(scanResult.getBleDevice().getMacAddress()))
.map(beacon -> beacon.toBuilder()
.lastSeen(System.currentTimeMillis())
.rssi(scanResult.getRssi())
.build());
} else {
return handleBeacon(scanResult)
.map(device -> (BluetoothDeviceApolo) device)
.switchIfEmpty(
handleDevice(scanResult).map(deviceApolo -> (BluetoothDeviceApolo) deviceApolo)
);
}
}
private Observable<DeviceApoloBeacon> handleBeacon(ScanResult scanResult) {
return Observable.fromCallable(() -> scanResult.getScanRecord().getManufacturerSpecificData(COMPANY_ID_APPLE))
.filter(bytes -> bytes != null)
.filter(bytes -> DeviceApoloBeacon.requiredManufactureSize == bytes.length)
.map(bytes -> DeviceApoloBeacon.builder()
.manufacturedData(bytes)
.lastSeen(System.currentTimeMillis())
.rssi(scanResult.getRssi())
.build())
.filter(beacon -> configuration.beacons().contains(beacon.uuuid()))
.doOnNext(beacon -> beaconsMap.put(scanResult.getBleDevice().getMacAddress(), beacon));
}
private Observable<DeviceApolo> handleDevice(ScanResult scanResult) {
final RxBleDevice rxBleDevice = scanResult.getBleDevice();
if (devicesMap.containsKey(rxBleDevice.getMacAddress())) {
return Observable.fromCallable(() -> devicesMap.get(rxBleDevice.getMacAddress()))
.timestamp()
.map(deviceStr -> DeviceApolo.create(deviceStr.getValue(), deviceStr.getTimestampMillis(), scanResult.getRssi()));
} else {
return readCharacteristic(rxBleDevice, scanResult.getRssi());
}
}
private Observable<DeviceApolo> readCharacteristic(RxBleDevice rxBleDevice, final int rssi) {
return rxBleDevice.establishConnection(false)
.compose(new ConnectionSharingAdapter())
.switchMap(rxBleConnection -> rxBleConnection.readCharacteristic(configuration.gattServer().characteristic()))
.map(String::new)
.doOnNext(s -> devicesMap.put(rxBleDevice.getMacAddress(), s))
.timestamp()
.map(deviceStr -> DeviceApolo.create(deviceStr.getValue(), deviceStr.getTimestampMillis(), rssi))
.retry();
}
}
'iOS 서버 앱 숨기기'란 무엇을 의미합니까? –
즉, iOS 어플리케이션이 GATT 서버를 생성 할 때 백그라운드로 이동합니다. – Alex
'Android 클라이언트가 BLE GATT 디바이스를 감지하지 못한다고 가정하면 스캔 할 때 디바이스를 방출하지 않는다는 것을 의미합니다. 'BLE Scanner app'이 단순히 iOS 앱이 포 그라운드에 있었던 시간부터 검색 결과를 캐시합니까? –