目前在iOS中藍(lán)牙開發(fā)框架主要有以下幾種
- GameKit.framework:iOS7之前的藍(lán)牙通訊框架,從iOS7開始過(guò)期.
- MultipeerConnectivity.framework:iOS7開始引入的新的藍(lán)牙通訊開發(fā)框架岳守,用于取代GameKit。
- CoreBluetooth.framework:功能強(qiáng)大的藍(lán)牙開發(fā)框架,要求設(shè)備必須支持藍(lán)牙4.0鼓择。
- 前兩個(gè)框架使用起來(lái)比較簡(jiǎn)單兑宇,但是缺點(diǎn)也比較明顯:僅僅支持iOS設(shè)備,傳輸內(nèi)容僅限于沙盒或者照片庫(kù)中用戶選擇的文件邢隧,并且第一個(gè)框架只能在同一個(gè)應(yīng)用之間進(jìn)行傳輸(一個(gè)iOS設(shè)備安裝應(yīng)用A修档,另一個(gè)iOS設(shè)備上安裝應(yīng)用B是無(wú)法傳輸?shù)模oreBluetooth就擺脫了這些束縛府框,它不再局限于iOS設(shè)備之間進(jìn)行傳輸吱窝,你可以通過(guò)iOS設(shè)備向Android讥邻、Windows Phone以及其他安裝有藍(lán)牙4.0芯片的智能設(shè)備傳輸,因此也是目前智能家居院峡、無(wú)線支付等熱門智能設(shè)備所推崇的技術(shù)兴使。本文主要介紹CoreBluetooth的相關(guān)開發(fā)流程.
CoreBluetooth開發(fā)模式
CoreBluetooth設(shè)計(jì)類似于客戶端-服務(wù)器端的設(shè)計(jì),peripheral和central, 可以理解成外設(shè)和中心照激。對(duì)應(yīng)他們分別有一組相關(guān)的API和類.
- 左側(cè)叫做中心模式发魄,以app作為中心,連接其他的外設(shè)的俩垃,而右側(cè)稱為外設(shè)模式励幼,使用手機(jī)作為外設(shè)別其他中心設(shè)備操作的場(chǎng)景。
-
服務(wù)和特征口柳,特征的屬性(service and characteristic):
藍(lán)牙設(shè)備添加若干服務(wù)苹粟,服務(wù)內(nèi)添加若干特征,特征就是具體鍵值對(duì)跃闹,提供對(duì)數(shù)據(jù)的讀取和寫嵌削。藍(lán)牙中心和外設(shè)數(shù)據(jù)的交換基于服務(wù)的特征.
中心的開發(fā)
- 流程
<pre>
- 建立中心角色
- 掃描外設(shè)(discover)
- 連接外設(shè)(connect)
- 掃描外設(shè)中的服務(wù)和特征(discover)
- 4.1 獲取外設(shè)的services
- 4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值,獲取 Characteristics的Descriptor和Descriptor的值
- 與外設(shè)做數(shù)據(jù)交互(explore and interact)
- 訂閱Characteristic的通知
- 斷開連接(disconnect)
</pre>
-
步驟
CoreBluetooth中不管是中心還是外設(shè)的開發(fā),均是上面的流程,通過(guò)一步步的代理回調(diào)實(shí)現(xiàn)的,按照流程跟著走,一步一步實(shí)現(xiàn)代理.
<pre>
//初始化
_centralManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:@{CBCentralManagerOptionRestoreIdentifierKey:kRestoreIdentifierKey}];
//監(jiān)聽(tīng)中心設(shè)備狀態(tài)
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
if (central.state == CBCentralManagerStatePoweredOn) {
[self writeToLogWithText:@"中心設(shè)備已打開"];
[_centralManager scanForPeripheralsWithServices:nil options:nil];
} //中心設(shè)備CBCentralManagerStatePoweredOn狀態(tài)下就可以開始搜索外設(shè)了
}
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
//搜索到指定的外設(shè)就可以開始連接外設(shè)了
[_centralManager connectPeripheral:peripheral options:nil];
}
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
//連接成功后開始搜索服務(wù)
[peripheral discoverServices:nil];
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
//搜索服務(wù)成功后查找指定服務(wù)中的特征
[peripheral discoverCharacteristics:nil forService:service];
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(nonnull CBService *)service error:(nullable NSError *)error{
//發(fā)現(xiàn)可用特征后,就可以進(jìn)行相應(yīng)的操作了,主要有以下三種
//情景一:讀取
if (characteristic.properties & CBCharacteristicPropertyRead) {
if ([characteristic.UUID.UUIDString isEqualToString:kReadUUID]) {
[peripheral readValueForCharacteristic:characteristic];
if (characteristic.value) {
NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(@"讀取到特征值:%@",value);
}
}
}//情景二:通知 if (characteristic.properties & CBCharacteristicPropertyNotify) { if ([characteristic.UUID.UUIDString isEqualToString:kNotifyUUID] || [characteristic.UUID.UUIDString isEqualToString:kWriteUUID]) { _curCharacter = characteristic; [peripheral setNotifyValue:YES forCharacteristic:characteristic]; [self writeToLogWithText:@"已訂閱特征通知"]; } } //情景二:寫數(shù)據(jù) if (characteristic.properties & CBCharacteristicPropertyWrite) { if ([characteristic.UUID.UUIDString isEqualToString:kWriteUUID]) { [peripheral writeValue:[@"hello,外設(shè)" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; [self writeToLogWithText:@"寫數(shù)據(jù)給外設(shè)"]; _curPeripherals = peripheral; _curCharacter = characteristic; } }
}
//根據(jù)不同的場(chǎng)景,會(huì)調(diào)用以下三個(gè)方法
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error;
}
</pre>
外設(shè)的開發(fā)
- 流程
<pre>- 打開peripheralManager望艺,設(shè)置peripheralManager的委托
- 創(chuàng)建characteristics苛秕,characteristics的description 創(chuàng)建service,把characteristics添加到service中找默,再把service添加到peripheralManager中
- 開啟廣播advertising
- 對(duì)central的操作進(jìn)行響應(yīng)
- 4.1 讀characteristics請(qǐng)求
- 4.2 寫characteristics請(qǐng)求
- 4.4 訂閱和取消訂閱characteristics
</pre>
-
步驟
<pre>
//初始化
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
//啟動(dòng)外設(shè)
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
//CBPeripheralManagerStatePoweredOn下添加服務(wù)和特征
[self setupService];
}
//創(chuàng)建服務(wù),特征并添加服務(wù)到外圍設(shè)備
-(void)setupService{//可通知的特征
// CBUUID *characteristicUUID = [CBUUID UUIDWithString:kNotifyUUID];
// _characteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];//可讀寫的特征
CBUUID *UUID2 = [CBUUID UUIDWithString:kWriteUUID];
_characteristic = [[CBMutableCharacteristic alloc] initWithType:UUID2 properties:CBCharacteristicPropertyWrite|CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsWriteEncryptionRequired];
//
//只讀的特征
// CBUUID *UUID3 = [CBUUID UUIDWithString:kReadUUID];
// NSData *characteristicValue = [@"aaron才" dataUsingEncoding:NSUTF8StringEncoding];
// _characteristic = [[CBMutableCharacteristic alloc] initWithType:UUID3 properties:CBCharacteristicPropertyRead value:characteristicValue permissions:CBAttributePermissionsReadable];CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
_service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
[_service setCharacteristics:@[_characteristic]];[_peripheralManager addService:_service];
}
//添加服務(wù)后開始廣播,等待中心設(shè)備的連接
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error{
NSDictionary *dict = @{CBAdvertisementDataLocalNameKey:kPeripheralName};
[_peripheralManager startAdvertising:dict];
[self writeToLogWithText:@"向外圍設(shè)備添加了服務(wù)"];
}
根據(jù)中心設(shè)備的響應(yīng),外設(shè)在代理中收到中心發(fā)來(lái)的信息,
//訂閱特征
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;
//取消訂閱
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;
//中心設(shè)備讀外設(shè)數(shù)據(jù)
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request;
//收到中心寫來(lái)的數(shù)據(jù)
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;
</pre>
UIBackgroundModes下的藍(lán)牙數(shù)據(jù)傳輸
CoreBluetooth提供了非常友好的Background支持.按一下步驟,經(jīng)過(guò)測(cè)試,app進(jìn)入后臺(tái)后,也能收到外設(shè)特征更新的值,據(jù)WWDC2013視頻介紹,就算因?yàn)閮?nèi)存緊張,app在后臺(tái)被殺掉,系統(tǒng)也會(huì)自動(dòng)幫我們重新啟動(dòng)app進(jìn)行藍(lán)牙數(shù)據(jù)傳輸,不過(guò)這個(gè)就沒(méi)測(cè)試到,不知道有沒(méi)有人做過(guò)這方便的研究.
設(shè)置info.plist
<pre>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>
</pre>
初始化方式中添加``options:@{CBCentralManagerOptionRestoreIdentifierKey:kRestoreIdentifierKey}``
<pre>
_centralManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:@{CBCentralManagerOptionRestoreIdentifierKey:kRestoreIdentifierKey}];
</pre>
<pre>
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *,id> *)dict{
// NSArray *scanServices = dict[CBCentralManagerRestoredStateScanServicesKey];
// NSArray *scanOptions = dict[CBCentralManagerRestoredStateScanOptionsKey];
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
for (CBPeripheral *peripheral in peripherals) {
[self.peripherals addObject:peripheral];
peripheral.delegate = self;
}
}
</pre>
<pre>
-
(void)centralManagerDidUpdateState:(CBCentralManager *)central{
if (central.state == CBCentralManagerStatePoweredOn) {
[self writeToLogWithText:@"中心設(shè)備已打開"];
[_centralManager scanForPeripheralsWithServices:nil options:nil];//03,檢查是否restore connected peripherals for (CBPeripheral *peripheral in _peripherals) { if (peripheral.state == CBPeripheralStateConnected) { NSUInteger serviceIdx = [peripheral.services indexOfObjectPassingTest:^BOOL(CBService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { return [obj.UUID isEqual:kServiceUUID]; }]; if (serviceIdx == NSNotFound) { [peripheral discoverServices:@[kServiceUUID]]; continue; } CBService *service = peripheral.services[serviceIdx]; NSUInteger charIdx = [service.characteristics indexOfObjectPassingTest:^BOOL(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { return [obj.UUID isEqual:kNotifyUUID]; }]; if (charIdx == NSNotFound) { [peripheral discoverCharacteristics:@[kNotifyUUID] forService:service]; continue; } CBCharacteristic *characteristic = service.characteristics[charIdx]; if (!characteristic.isNotifying) { [peripheral setNotifyValue:YES forCharacteristic:characteristic]; } } }
}else{
[_peripherals removeAllObjects];
}
}
</pre>
<pre>
-
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.NSArray *peripheralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothPeripheralsKey];
NSArray *centraManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];for (NSString *identifier in centraManagerIdentifiers) {
if ([identifier isEqualToString:kRestoreIdentifierKey]) {}
}
return YES;
}
</pre>
Demo
不動(dòng)手寫代碼的學(xué)框架都是耍流氓,為了更好的學(xué)習(xí),我也是自己都敲了一遍代碼,前文中的三種場(chǎng)景以及后臺(tái)模式都實(shí)現(xiàn)了,可以實(shí)現(xiàn)中心設(shè)備和外設(shè)之前的訂閱特征,讀寫數(shù)據(jù)等功能.希望大家都交流.下面是傳送門以及界面展示??
github傳送門 CoreBluetoothDemo
![中心設(shè)備]IMG_3660.PNG](http://upload-images.jianshu.io/upload_images/1093584-e66b42d6c1c7133f.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)