如何基于RxAndroidBle实现扫描指定UUID的iBeacon并获取广播数据
如何使用RxAndroidBle扫描特定UUID的iBeacon?
我在Android平台使用RxAndroidBle库进行蓝牙扫描,现有扫描方法无法识别我的iBeacon(仓库已说明原因),但我不清楚如何修改方法来扫描我的iBeacon。
原扫描方法:
public static void startScanning() { if (isScanning()) { return; } final BluetoothClient instance = getInstance(); Log.d(TAG, "Starting to scan for beacons"); ScanSettings scanSettings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .build(); instance.scanningSubscription = instance.rxBleClient.scanBleDevices(scanSettings) .subscribe(new Observer<ScanResult>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.e(TAG, "Bluetooth scanning error", e); } @Override public void onNext(ScanResult scanResult) { instance.processScanResult(scanResult); } }); }
我尝试添加ScanFilter来匹配iBeacon的UUID,但这个实现无效,无法过滤出目标设备:
ScanSettings scanSettings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .build(); ScanFilter scanFilter = new ScanFilter.Builder() .setServiceUuid(UUID).build(); instance.scanningSubscription = instance.rxBleClient.scanBleDevices(scanSettings, scanFilter) .subscribe(new Observer<ScanResult>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.e(TAG, "Bluetooth scanning error", e); } @Override public void onNext(ScanResult scanResult) { instance.processScanResult(scanResult); } });
我家中的4个iBeacon UUID均为EBEFD083-70A2-47C8-9837-E7B5634DF524,请问如何修改该方法以正确扫描此特定UUID的iBeacon,或者如何封装iBeacon API以获取仓库预期的必要广播数据?
问题根源
你用setServiceUuid()来过滤iBeacon是行不通的——因为iBeacon的广播并不是基于GATT服务UUID的,它是苹果定义的一种制造商特定广播数据格式,使用的是苹果的公司ID(0x004C),所以系统的ScanFilter无法通过服务UUID直接匹配iBeacon。
解决方案:两种可行的扫描方式
方式1:先扫描所有设备,再在回调中过滤iBeacon
这种方式不需要修改扫描设置,而是在onNext()中解析扫描结果的广播数据,判断是否是目标UUID的iBeacon:
public static void startScanning() { if (isScanning()) { return; } final BluetoothClient instance = getInstance(); final UUID TARGET_IBeACON_UUID = UUID.fromString("EBEFD083-70A2-47C8-9837-E7B5634DF524"); Log.d(TAG, "Starting to scan for beacons"); ScanSettings scanSettings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .build(); instance.scanningSubscription = instance.rxBleClient.scanBleDevices(scanSettings) .subscribe(new Observer<ScanResult>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.e(TAG, "Bluetooth scanning error", e); } @Override public void onNext(ScanResult scanResult) { // 解析广播数据,判断是否是目标iBeacon if (isTargetIBeacon(scanResult, TARGET_IBeACON_UUID)) { instance.processScanResult(scanResult); } } }); } // 解析iBeacon广播数据的工具方法 private static boolean isTargetIBeacon(ScanResult scanResult, UUID targetUuid) { BluetoothDevice device = scanResult.getDevice(); ScanRecord scanRecord = scanResult.getScanRecord(); if (scanRecord == null) return false; byte[] manufacturerData = scanRecord.getManufacturerSpecificData(0x004C); // 苹果公司ID if (manufacturerData == null || manufacturerData.length < 25) return false; // iBeacon数据至少25字节 // 验证iBeacon的类型(前2字节是0x0215) if (manufacturerData[0] != 0x02 || manufacturerData[1] != 0x15) return false; // 提取UUID部分(字节2-17) byte[] uuidBytes = Arrays.copyOfRange(manufacturerData, 2, 18); UUID beaconUuid = uuidBytesToUuid(uuidBytes); return beaconUuid.equals(targetUuid); } // 将字节数组转换为UUID的工具方法 private static UUID uuidBytesToUuid(byte[] bytes) { ByteBuffer bb = ByteBuffer.wrap(bytes); long high = bb.getLong(); long low = bb.getLong(); return new UUID(high, low); }
方式2:使用制造商数据的ScanFilter(API 21+)
如果你的最低API版本是21及以上,可以直接构建匹配苹果制造商数据和目标UUID的ScanFilter,减少不必要的回调:
public static void startScanning() { if (isScanning()) { return; } final BluetoothClient instance = getInstance(); UUID targetUuid = UUID.fromString("EBEFD083-70A2-47C8-9837-E7B5634DF524"); Log.d(TAG, "Starting to scan for beacons"); // 构建iBeacon制造商数据前缀:0x0215 + UUID字节 ByteBuffer manufacturerDataBuffer = ByteBuffer.allocate(18); manufacturerDataBuffer.put((byte) 0x02); // iBeacon类型长度 manufacturerDataBuffer.put((byte) 0x15); // iBeacon类型 manufacturerDataBuffer.putLong(targetUuid.getMostSignificantBits()); manufacturerDataBuffer.putLong(targetUuid.getLeastSignificantBits()); byte[] manufacturerDataPrefix = manufacturerDataBuffer.array(); ScanFilter scanFilter = new ScanFilter.Builder() .setManufacturerData(0x004C, manufacturerDataPrefix, null) // 匹配前缀,后缀不限制 .build(); ScanSettings scanSettings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .build(); instance.scanningSubscription = instance.rxBleClient.scanBleDevices(scanSettings, scanFilter) .subscribe(new Observer<ScanResult>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.e(TAG, "Bluetooth scanning error", e); } @Override public void onNext(ScanResult scanResult) { instance.processScanResult(scanResult); } }); }
补充说明
- RxAndroidBle本身并没有专门封装iBeacon的解析逻辑,所以需要你自己处理广播数据的解析,或者可以引入专门的iBeacon解析库来简化操作,但如果只需要基础的UUID匹配,上面的工具方法足够。
- 注意权限:确保你的应用已经申请了
BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT权限(Android 12+),或者旧版本的ACCESS_FINE_LOCATION权限,并且定位服务已经开启。
内容的提问来源于stack exchange,提问作者LucasHood
相关产品推荐
相关产品推荐

