CBCentralManagerScanOptionAllowDuplicatesKey设为false仍重复发现蓝牙设备原因及解决
CBCentralManager重复扫描到同一设备的原因与解决方案
核心原因
CBCentralManagerScanOptionAllowDuplicatesKey 设为 false 时,苹果系统仅会对广播数据无变化、且在系统扫描缓存有效期内的同一外设去重,出现重复回调通常是以下情况导致:
- 外设广播数据动态变化:只要外设的广播包内容(包括设备名、服务UUID、厂商自定义数据、信号强度等任意字段)发生变更,系统会将其判定为新的扫描结果,再次触发回调。很多物联网外设会在广播包中携带动态数据(比如传感器实时数值、连接状态标记),就会频繁触发重复回调。
- 扫描操作被重复调用:你当前的代码在
centralManagerDidUpdateState回调中每次检测到蓝牙状态为开启就直接发起扫描,如果该方法被多次触发(比如蓝牙状态临时波动、应用前后台切换触发状态刷新),重复调用scanForPeripherals会清空系统之前的扫描缓存,已经扫描到的设备会被当做新结果再次回调。 - 系统扫描策略限制:应用切后台后系统会修改扫描策略,或者蓝牙模块缓存被系统主动清理时,也会导致已经扫到的设备再次触发回调。
解决方案
- 本地自行实现去重逻辑:这是最稳妥的兼容方案,无需依赖系统的去重策略。声明一个集合存储已经扫描到的外设唯一标识
identifier,每次didDiscover回调触发时先做判断,不存在于集合中再执行业务逻辑,示例代码如下:
// 全局声明去重缓存 var discoveredPeripheralIds = Set<UUID>() func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { // 已经扫描过的设备直接忽略 guard !discoveredPeripheralIds.contains(peripheral.identifier) else { return } // 新设备加入缓存,执行业务逻辑 discoveredPeripheralIds.insert(peripheral.identifier) // 后续处理逻辑... } // 重新扫描前记得清空缓存 func restartScan() { discoveredPeripheralIds.removeAll() centralManager.stopScan() centralManager.scanForPeripherals(withServices: nil, options: [ CBCentralManagerScanOptionAllowDuplicatesKey: false ]) }
- 优化扫描调用逻辑:新增扫描状态标记,避免重复发起扫描,调整后的扫描启动代码如下:
var isScanning = false func centralManagerDidUpdateState(_ central: CBCentralManager) { self.bluetoothStatus = (central.state == .poweredOn) if bluetoothStatus && !isScanning { isScanning = true // 扫描前清空旧缓存 discoveredPeripheralIds.removeAll() centralManager.scanForPeripherals(withServices: nil, options: [ CBCentralManagerScanOptionAllowDuplicatesKey: false ]) } } // 扫描结束后重置标记 func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { central.stopScan() isScanning = false }
- 不需要持续扫描时及时调用
stopScan()停止扫描,从根源避免多余回调。
内容的提问来源于stack exchange,提问作者Conrad Scherb
相关产品推荐
相关产品推荐

