如何在后台和前台使用iOS 7.1中的蓝牙LE检测附近的设备?

我有一个应用程序,需要检测附近(范围内的蓝牙LE)设备运行相同的应用程序和iOS 7.1。 我考虑过两种检测方法:

  1. 使设备充当iBeacons并检测范围内的iBeacons
  2. 使用CoreBluetooth(就像这里的 Vicinity实现一样)创build一个BLE外设,做广告并扫描外设

看来选项1是不可能的,因为:

  • 当iOS应用程序运行后台(iOS 7.1)时,iOS可能至less需要15分钟才能检测到进入信标区域。

备选scheme2似乎是要走的路,但执行方面有一些困难:

  • iOS似乎在一段时间后(大约15分钟?)更改广告数据包中的外设UUID。 这意味着不能直接从广告广播信号中识别广告装置。

对此,我有以下问题:

  • 有没有其他的方法来实现附近的设备检测我没有考虑?
  • 是否有可能通过广告(或通过其他方式)识别设备,以便选项2可以工作?

我发现了一个使Core蓝牙(选项2)工作的方法,程序大致如下:

  • 应用程序通过CBAdvertisementDataLocalNameKey的编码设备唯一标识符(当广播应用程序运行前景时)和通过蓝牙LE服务提供设备唯一标识符的特征(当广播应用程序运行后台时)
  • 同时,应用程序使用相同的服务扫描其他外围设备

广告的作品如下:

  • 对于其他设备能够识别这个设备,我使用每个设备唯一的UUID(我使用的是Urban Airship的[UAUtils deviceID] ,因为它是程序其他部分的设备标识符,但是也可以以及使用任何唯一的ID实现)。
  • 当应用程序运行在前台时,我可以使用CBAdvertisementDataLocalNameKey 直接在广告数据包中传递设备唯一标识。 标准的UUID表示太长,所以我使用缩写的UUIDforms如下:

     + (NSString *)shortenedDeviceID { NSString *deviceID = [UAUtils deviceID]; NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:deviceID]; uuid_t uuidBytes; [uuid getUUIDBytes:uuidBytes]; NSData *data = [NSData dataWithBytes:uuidBytes length:16]; NSString *base64 = [data base64EncodedStringWithOptions:0]; NSString *encoded = [[[base64 stringByReplacingOccurrencesOfString:@"/" withString:@"_"] stringByReplacingOccurrencesOfString:@"+" withString:@"-"] stringByReplacingOccurrencesOfString:@"=" withString:@""]; return encoded; } 
  • 当应用程序运行后台时,广告数据包被剥离,并且CBAdvertisementDataLocalNameKey不再被传递。 为此,应用程序需要发布一个提供唯一设备标识符的特性:

     - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { if (peripheral.state == CBPeripheralManagerStatePoweredOn) { [self startAdvertising]; if (peripheralManager) { CBUUID *serviceUUID = [CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:DEVICE_IDENTIFIER_CHARACTERISTIC_UUID]; CBMutableCharacteristic *characteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID properties:CBCharacteristicPropertyRead value:[[MyUtils shortenedDeviceID] dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable]; CBMutableService *service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; service.characteristics = @[characteristic]; [peripheralManager addService:service]; } } } 

扫描工作如下:

  • 您开始扫描具有特定服务UUID的外围设备,如下所示(请注意,您需要指定服务UUID,否则后台扫描无法find该设备):

     [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]] options:scanOptions]; 
  • 当设备被发现在- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI你检查,如果advertisementData[CBAdvertisementDataLocalNameKey]存在,并尝试将其转换回UUIDforms,如下所示:

     + (NSString *)deviceIDfromShortenedDeviceID:(NSString *)shortenedDeviceID { if (!shortenedDeviceID) return nil; NSString *decoded = [[[shortenedDeviceID stringByReplacingOccurrencesOfString:@"_" withString:@"/"] stringByReplacingOccurrencesOfString:@"-" withString:@"+"] stringByAppendingString:@"=="]; NSData *data = [[NSData alloc] initWithBase64EncodedString:decoded options:0]; if (!data) return nil; NSUUID *uuid = [[NSUUID alloc] initWithUUIDBytes:[data bytes]]; return uuid.UUIDString; } 
  • 如果转换失败,您知道广播设备处于后台,您需要连接到设备以读取提供唯一标识符的特征。 为此,您需要使用[self.central connectPeripheral:peripheral options:nil]; (使用peripheral.delegate = self;并按如下方式实现一系列委托方法:

     - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { [peripheral discoverServices:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]]]; } - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { if (!error) { for (CBService *service in peripheral.services) { if ([service.UUID.UUIDString isEqualToString:DEVICE_IDENTIFIER_SERVICE_UUID]) { NSLog(@"Service found with UUID: %@", service.UUID); [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_CHARACTERISTIC_UUID]] forService:service]; } } } } - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { if (!error) { for (CBCharacteristic *characteristic in service.characteristics) { if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:DEVICE_IDENTIFIER_CHARACTERISTIC_UUID]]) { [peripheral readValueForCharacteristic:characteristic]; } } } } - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (!error) { NSString *shortenedDeviceID = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]; NSString *deviceId = [MyUtils deviceIDfromShortenedDeviceID:shortenedDeviceID]; NSLog(@"Got device id: %@", deviceId); } }