我有一个包含图像数据的特征值。在外围设备中我设置了如下值:
_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
properties:CBCharacteristicPropertyRead
value:Nil
permissions:CBAttributePermissionsReadable];
我的理解是,当请求此值时,将调用didRecieveRequest回调:
-(void) peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:_photoUUID]) {
if (request.offset > request.characteristic.value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
else {
// Get the photos
if (request.offset == 0) {
_photoData = [NSKeyedArchiver archivedDataWithRootObject:_myProfile.photosImmutable];
}
request.value = [_photoData subdataWithRange:NSMakeRange(request.offset, request.characteristic.value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}
}
}
这几乎来自Apple的文档。在didDiscoverCharacteristic回调的中央一侧,我有以下代码:
if ([characteristic.UUID isEqual:_photoUUID]) {
_photoCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
而这反过来调用didUpdateValueCorCharacteristic回调:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@"updated value for characteristic");
if ([characteristic.UUID isEqual:_photoUUID]) {
NSArray * photos = [NSKeyedUnarchiver unarchiveObjectWithData:characteristic.value];
}
}
调用所有回调但是当我尝试重新构造数组时,它已损坏,因为并非所有数据都被正确传输。我希望didRecieveReadRequest回调可以多次调用,每次都有不同的偏移量。然而它只被召唤一次。
我想知道是否有人知道我做错了什么?
我猜你正在突破特征长度的512字节限制。您需要转到订阅特征和处理更新以解决此问题:
-[CBPeripheral setNotifyValue:forCharacteristic]
订阅特征(使用YES作为通知值)。-peripheral:didUpdateValueForCharacteristic:error
中,每次更新都将是要附加的数据,或者您选择在外围端使用以指示数据结束的内容(我使用空的NSData
)。更新您的-peripheral:didUpdateValueForCharacteristic:error
代码,以便:
如果您开始读取值,请为传入的字节初始化接收器(例如,NSMutableData
)。
如果您正在读取值,则会附加到接收器。
如果您看到EOD标记,则认为传输已完成。您可以通过调用通知值为NO的-[CBPeripheral setNotifyValue:forCharacteristic]
来取消订阅此状态下的特征。-peripheral:didUpdateNotificationStateForCharacteristic:error:
是管理初始化和以后使用读取块的接收器的好地方。如果characteristic.isNotifying
更新为YES
,您有新的订阅;如果它更新到NO
然后你已经读完了。此时,您可以使用NSKeyedUnarchiver取消归档数据。-[CBMutableCharacteristic initWithType:properties:value:permissions]
,确保properties
值包括CBCharacteristicPropertyNotify
。-peripheralManager:central:didSubscribeToCharacteristic:
启动数据的分块发送,而不是-peripheral:didReceiveReadRequest:result:
。central.maximumUpdateValueLength
。在iOS7上,在iPad 3和iPhone 5之间,我通常看到132个字节。如果您要发送到多个中心,请使用最小公共值。-updateValue:forCharacteristic:onSubscribedCentrals
的返回码;如果底层队列备份,这将返回NO
,你必须等待-peripheralManagerIsReadyToUpdateSubscribers:
上的回调才能继续(我认为这是其他平滑API中的一个毛刺)。根据你如何处理这个问题,你可以把自己画成一个角落,因为:-updateValue:forCharacteristic:onSubscribedCentrals:
的返回值,那么很容易让自己陷入非明显的死锁状态。您要么确保在每次调用-updateValue:forCharacteristic:onSubscribedCentrals:
之后产生队列,请在与外围队列不同的队列上执行组块循环(-updateValue:forCharacteristic:onSubscribedCentrals:
将确保其工作在正确的位置完成)。或者你可以变得更加漂亮;请注意这一点。为了看到这一点,WWDC 2012高级核心蓝牙视频包含一个涵盖大部分内容的示例(共享VCards)。但是,它不会检查更新的返回值,因此它们完全避免了#4中的陷阱。
希望有所帮助。