CoreBluetooth中央管理器回调函数didDiscoverPeripheral被调用两次。

10

我这样扫描我的外设:

NSDictionary *scanOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] 
                                                            forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
        // Scan for peripherals with given UUID
        [cm scanForPeripheralsWithServices:[NSArray arrayWithObject:HeliController.serviceUUID] options:scanOptions]

没问题,我找到了外围设备并成功连接。正如您所看到的,我使用了CBCentralManagerScanOptionAllowDuplicatesKeybool NO来防止多个外围设备连接,但有时会发现didDiscoverPeripheral回调函数会触发两次。

- (void) centralManager:(CBCentralManager *)central 
  didDiscoverPeripheral:(CBPeripheral *)peripheral 
  advertisementData:(NSDictionary *)advertisementData 
               RSSI:(NSNumber *)RSSI 
{        
if(!discovered){
    discovered = YES;
    NSLog(@"Discovered");

    [cm stopScan];

    [scanButton setTitle:@"Connect" forState:UIControlStateNormal];
}
else if(discovered){
    discovered = YES
    NSLog(@"Already discovered");
}
}

有时我会得到

Discovered
Already discovered
在我的控制台中输出,大多数情况下只有Discovered信息被显示。在我的外围设备委托方法中,我首先发现服务,然后调用[peripheral discoverCharacteristics,回调函数总是会被触发:
- (void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{

NSLog(@"Did discover characteristic for service %@", [service.peripheral UUID]);

for(CBCharacteristic *c in [service characteristics]){
    // We never get here when peripheral is discovered twice
    if([[c UUID] isEqual:myCharacteristicUUID]){

        NSLog(@"Found characteristic");

        self.throttleCharacteristic = c;

    }
}

didDiscoverPeripheral方法发生两次时,即使peripheral仍然存在(UUID和名称仍然正确),在该方法中service会变成nil

重新启动手机或重置网络设置可以暂时解决这个问题。

我真的需要解决这个问题!谢谢。


你给出的“不超过一个设备”的参数是什么? - yuklai
CBCentralManagerScanOptionAllowDuplicatesKey上的false - chwi
我的猜测是在信号弱的情况下它被调用了两次。 - tc.
我将在周一尝试那个理论,但我的经验是,如果它发生过一次,除非重新启动手机,否则我无法再让它工作。 - chwi
它与信号强度无关。 - chwi
2个回答

12

设备在广告时可能返回附加数据。 这些数据可能在不同的时间以单独的数据包到达。 在这种情况下,当首次检测到设备时会首先调用didDiscoverPeripheral,然后在其附加信息可用时再次调用它。

CBCentralManagerScanOptionAllowDuplicatesKey有所不同。 它告诉CoreBluetooth是否要在设备再次广告时接收重复结果。 它不能防止对相同发现序列多次调用didDiscoverPeripheral; 它仅防止重复的发现序列。

来源:http://lists.apple.com/archives/bluetooth-dev/2012/Apr/msg00047.html(bluetooth-dev上的苹果代表)。


@Wilhelmsen:需要解决什么问题?只需跟踪您已经看到的UUID即可。 - Glenn Maynard
我尝试了@Mike的答案,那是否也跟踪UUID?我对此还比较新,所以如果您对如何通过编程实现这一点有任何进一步的提示,我将非常感激!无论如何,非常感谢您提供的信息。 - chwi
1
didDiscoverPeripheral返回CBPeripheral对象。每个对象都有一个UUID属性,您可以使用它来跟踪回调在同一外围设备对象上发生的第一次和第二次时间。 - yuklai
“Additional data”被称为扫描响应,只有在中央设备请求时,外围设备才会发送该附加数据。在这种情况下,CoreBluetooth会自动为它发现的每个设备请求该数据。 - Marcus Adams
我一直在寻找这个,但我不确定。如何检查第二次我们执行didDiscoverPeripheral是否为附加数据?在我的情况下,广告数据是相同的。 - SeikoTheWiz

6
我认为这个参数并不是你所想象的那样。从苹果公司健康温度计等示例中观察到,打开此标记允许发现具有相同UUID的多个不同外设。例如,如果您想编写一个应用程序,在同一房间查看四个不同的温度计并找到它们,您需要使用此参数,以便扫描在找到第一个后不会停止。
在他们的代码中,苹果公司避免了重复这种情况:
NSMutableArray *peripherals = [self mutableArrayValueForKey:@"thermometers"];
if( ![self.thermometers containsObject:peripheral] )
    [peripherals addObject:peripheral];

如果设备已存在于数组中,则不会重复添加。
如果文档在这一点上更清晰就好了。我承认,我是根据参数在上下文中的使用方式猜测的。

是的,无论如何,这就是苹果放置它的地方。请参阅http://developer.apple.com/library/mac/#samplecode/HealthThermometer/Listings/HealthThermometerClient_HealthThermometerClientAppDelegate_m.html#//apple_ref/doc/uid/DTS40011370-HealthThermometerClient_HealthThermometerClientAppDelegate_m-DontLinkElementID_4以获取完整的源代码。 - Mike
我认为问题已经解决了。我无论如何都无法复制它 :D 我会在我的问题上发布一个答案来澄清。非常感谢你。 - chwi
1
这是不正确的。不同的外设永远不会有相同的UUID;如果有四个温度计,就有四个UUID。设置CBCentralManagerScanOptionAllowDuplicatesKey允许同一设备多次返回,因此只要设备正在广告并且您尚未连接到它,您将一遍又一遍地收到didDiscoverPeripheral回调。将其设置为NO(默认值)意味着您只想在第一次看到它。 - Glenn Maynard
3
周边 UUID 和服务 UUID 存在区别。scanForPeripheralsWithServices:options: 使用的是服务 UUID,这个服务UUID 可能会在提供相同信息(例如体温计)的多个设备之间重复使用。该调用不使用设备本身的 UUID,设备本身的 UUID 是另一个问题。我认为(但不确定)它们是唯一的。原帖没有清楚地说明这一点。 - Mike
但是在后台扫描时,CBCentralManagerScanOptionAllowDuplicatesKey会被忽略。我该如何在后台连接这4个温度计,因为iOS将把它们合并成一个数据包并作为一个广告呈现?@Mike - Shashank Agarwal
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接