藍牙技術

關鍵詞.PNG

其他文章
藍牙一 有關代理協(xié)議詳解 和 info.plist設置


第一部分:藍牙概述
第二部分:central角色的實現(xiàn)
第三部分:peripheral角色的實現(xiàn)
第四部分:iOS藍牙應用的后臺處理
第五部分:與peripheral通信的最佳實踐
第六部分:作為Peripheral端的最佳方式


一、藍牙概述

1.1 centralperipheral在藍牙通訊中的關系

centralperipheral設備及他們在藍牙通訊中的角色。
藍牙通訊中的兩個重要的角色,centralperipheral。相對于傳統(tǒng)的client-server架構,peripheral代表著擁有數(shù)據(jù)的一方并且外界是需要這數(shù)據(jù)的。central代表著使用數(shù)據(jù)方,從peripheral那里獲得數(shù)據(jù)并完成特定的事情。
centrals發(fā)現(xiàn)并連接到正在廣播的peripherals
peripheral在廣播包中廣播他們的數(shù)據(jù)。廣播包中應該包含一些有用的信息,比如peripheral的名稱和主要功能等。
central可以掃描和監(jiān)聽任何他感興趣的peripheral設備。

Central掃描和監(jiān)聽感興趣的Peripheral.PNG
1.2peripheral如何組織數(shù)據(jù)

peripheral可以包含一個或多個services或提供它們連接信號強度的信息。service是數(shù)據(jù)和輔助行為的集合。
service本身由characteristics和引用的services組成。characteristic提供服務的詳細的信息。

Service的組成.PNG
1.3 central檢索并與peripheral交互數(shù)據(jù)

建立連接之后,就可以發(fā)現(xiàn)所有的servicescharacteristics(廣播中可能只包含部分services

central可以通過servicescharacteristic來讀或寫數(shù)據(jù)。

1.4 centralsperipheralsperipheral的數(shù)據(jù)如何表示

角色和數(shù)據(jù)在藍牙框架中以一種簡單直接的方式表示。

1.4.1 central端的對象

當你使用central來與peripheral通訊,你執(zhí)行的是central端的操作。
central端,設備用CBCentralManager對象代表。這個對象用來管理被發(fā)現(xiàn)的已連接的peripherals。

藍牙框架中如何表示central和peripheral.PNG

peripheral的數(shù)據(jù)用CBServiceCBCharacteristic來表示。
當你與peripheral交互時,你需要使用他的sevicescharacteristics。
在藍牙sdkserviceCBService對象表示,characteristicCBCharacteristic對象表示。

CBPeripheral的結構.PNG
1.4.2 peripheral 端的對象

如果是安裝OS X v10.9iOS6,那你的macios設備就可以作為peripheral端設備,為其他設別提供數(shù)據(jù)。讓你的設備實現(xiàn)peripheral角色,執(zhí)行peripheral端的功能。

本地peripherals和遠端centrals
peripheral端,peripheral設備用CBPeripheralManager對象表示。這個對象用來管理發(fā)布servicescharacteristics,并發(fā)出廣播。同時peripheral manager可用來響應讀和寫請求。

CBCentral 和 CBPeripheralManager 的關系.PNG

本地peripheral的數(shù)據(jù)用CBMutableServiceCBMutableCharacteristic對象表示

當你創(chuàng)建并想設置peripheral的數(shù)據(jù)。你需要處理peripheral manager 對象的services (CBMutableService實例),同樣,characteristic的實例也是CBMutableCharacteristic對象。

CBMutableService 和 CBMutableCharacteristic 的關系.PNG

二、central角色的實現(xiàn)

2.1 central角色的實現(xiàn)

central角色需要完成的幾件事情,如 :搜索,連接,與peripheral交互數(shù)據(jù)。
peripheral角色同樣需要完成幾件事情,如:發(fā)布和廣播服務,響應讀,寫,訂閱請求

接下來,你將學習如何完成central端的功能。

  • 1,創(chuàng)建central manager 對象
  • 2,發(fā)現(xiàn)和連接正在廣播的peripheral
  • 3,瀏覽peripheral的數(shù)據(jù)
  • 4,發(fā)送讀和寫請求到peripheral設備
  • 5,訂閱characteristic的值
2.1.1 創(chuàng)建Central manager對象

引入頭文件#import <CoreBluetooth/CoreBluetooth.h>
遵守協(xié)議方法<CBCentralManagerDelegate,CBPeripheralDelegate>

myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];

方法說明:
在這里self設置成central角色的代理。dispath queue設置為nil,意味著central事件將交由main queue處理。

創(chuàng)建central manager時,會觸發(fā)centralManagerDidUpdateState:代理方法。你必須實現(xiàn)這個代理。



// 中心設備的藍牙狀態(tài)發(fā)生變化之后會調用此方法 [必須實現(xiàn)的方法]
 - (void)centralManagerDidUpdateState:(CBCentralManager *)central;
 // 中心設備狀態(tài)枚舉 
typedef NS_ENUM(NSInteger, CBCentralManagerState) { 
CBCentralManagerStateUnknown = CBManagerStateUnknown,// 藍牙狀態(tài)未知
CBCentralManagerStateResetting = CBManagerStateResetting, 
CBCentralManagerStateUnsupported = CBManagerStateUnsupported, // 不支持藍牙 
CBCentralManagerStateUnauthorized = CBManagerStateUnauthorized, // 藍牙未授權 CBCentralManagerStatePoweredOff = CBManagerStatePoweredOff, // 藍牙關閉狀態(tài) 
CBCentralManagerStatePoweredOn = CBManagerStatePoweredOn, // 藍牙開啟狀態(tài)
 } NS_DEPRECATED(NA, NA, 5_0, 10_0, "Use CBManagerState instead");
2.1.2 搜索正在發(fā)送廣播的peripheral
[myCentralManager scanForPeripheralsWithServices:nil options:nil];

方法說明:
注意:如果第一個參數(shù)設置成nil,那么centralmanager會返回所有被發(fā)現(xiàn)的peripherals,在實際應用中,你應該給他賦值 CBUUID 對象數(shù)組。這樣只有有廣播這些uuid服務的peripheral才會被返回,一旦發(fā)現(xiàn)peripheral,將觸發(fā)centralManager:didDiscoverPeripheral:advertisementData:RSSI:代理方法,如果你想連接這個peripheral,請使用強引用變量引用它,這樣系統(tǒng)就不會釋放掉它。

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral 
advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    NSLog(@"Discovered %@", peripheral.name); 
    self.discoveredPeripheral = peripheral; 
    ... 
}

如果你想連接多個設備,你可以使用NSArray來管理他們。不管什么情況,一旦你找到所有你想要的設備,你應該停止掃描以便節(jié)省電量。
[myCentralManager stopScan];

2.1.3 連接peripheral

[myCentralManager connectPeripheral:peripheral options:nil];
建議使用下面這種連接外設方式

 [self.manager connectPeripheral:peripheral
                                options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
}

options:參數(shù)說明

CBConnectPeripheralOptionNotifyOnConnectionKey: 在應用掛起后,與指定的peripheral成功建立連接,則發(fā)出通知(在連接成功后,程序被掛起,給出系統(tǒng)提示。 
)
CBConnectPeripheralOptionNotifyOnDisconnectionKey: 在應用掛起后,如果與指定的peripheral斷開連接,則發(fā)出通知(在程序掛起,藍牙連接斷開時,給出系統(tǒng)提示。 
)
CBConnectPeripheralOptionNotifyOnNotificationKey: 在應用掛起后,指定的peripheral有任何通知都進行提示,建議使用這個( 在程序掛起后,收到 peripheral 數(shù)據(jù)時,給出系統(tǒng)提示。 Core Bluetooth 后臺模式 
)

如果連接成功,則會觸發(fā)centralManager:didConnectPeripheral:代理方法,與之通訊之前,你需要為它設置代理

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"Peripheral connected");
    peripheral.delegate = self; // 設置代理
    ...
}
2.1.4 查詢peripheral的服務

建立連接之后,就可以查詢數(shù)據(jù),第一步就是查詢有哪些services。因為廣播包有大小限制,在廣播中可能沒有顯示全部的服務信息,這里你可以使用discoverServices:來查詢所有的services。
[peripheral discoverServices:nil];
注意:實際開發(fā)中,你不應該傳值nil,因為這樣做會返回所有的services,包括那些你不需要的services,這樣做會浪費時間和電量。所以你應該傳遞你需要的uuids。

查找到所有的服務會觸發(fā)peripheral:didDiscoverServices:代理方法

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    for (CBService *service in peripheral.services) {
     NSLog(@"Discovered service %@", service);
     ...
}
2.1.5 查找characteristics

發(fā)現(xiàn)service之后,下一步就是查找characteristics

NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];

注意:實際開發(fā)中,不要傳nil,這樣做會返回所有的characteristics,包括那些你不感興趣的characteristics。這樣做即浪費時間又損耗電量。所以你應該傳你需要的uuids的值

查找到characteristics之后,會觸發(fā)peripheral:didDiscoverCharacteristicsForService:error:代理方法

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    for (CBCharacteristic *characteristic in service.characteristics) {
    NSLog(@"Discovered characteristic %@", characteristic);
    ...
}
2.1.6 獲取數(shù)據(jù)

獲取到數(shù)據(jù)后peripheral調用peripheral:didUpdateValueForCharacteristic:error:代理方法

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    NSData *data = characteristic.value;
    // parse the data as needed
    ... 
}

一個characteristic只包含一種信息數(shù)據(jù)。比如恒溫service下的溫度characteristic只包含當前溫度值,你可以通過讀取或訂閱來獲得這個值。

####### 2.1.6.1 讀取characteristic的數(shù)據(jù)

NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];

注意:并不是所有的characteristic都是可讀的,你可以通過 characteristicproterties屬性是否包含CBCharacteristicPropertyRead常量來判斷是否可讀。在讀一個不可讀的characteristic的數(shù)據(jù)值,會在代理方法的error參數(shù)中體現(xiàn)異常信息

####### 2.1.6.2訂閱characteristic的值
雖然通過readValueForCharacteristic: 可以有效獲取靜態(tài)值,但如果值是動態(tài)改變的,則最好使用訂閱的方法。當值變化時,自動獲得通知。
設置訂閱

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

訂閱的時候peripheral會調用peripheral:didUpdateNotificationStateForCharacteristic:error:代理方法。如果訂閱失敗了,也可以通過這個方法查詢失敗的原因。

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"Error changing notification state: %@",[error localizedDescription]);
    }
}

注意:并不是所有的characteristics都可以訂閱,可以通過檢查characteristicproperties屬性是否包含CBCharacteristicPropertyNotifyCBCharacteristicPropertyIndicate常量來判斷是否可訂閱。

2.1.7 寫入數(shù)據(jù)

有時也是需要寫數(shù)據(jù)的,比如恒溫器,你需要設置目標溫度。如果characteristic是可寫的,那么你就可以調用writeValue:forCharacteristic:type:方法來寫入數(shù)據(jù)。如下:

NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic type:CBCharacteristicWriteWithResponse];

當你寫數(shù)據(jù)時,你可以標明寫類型。上例中,寫類型是CBCharacteristicWriteWithResponse, 這種情況下,不管有沒有寫成功,peripheral都會通過代理通知你。你需要實現(xiàn)這個方法以便處理異常情況。

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"Error writing characteristic value: %@",[error localizedDescription]);
    }
}

如果你設置的寫類型是CBCharacteristicWriteWithoutResponse, 那么寫操作會以更有效的方式執(zhí)行,但不保證寫成功,并且不會有報告。peripheral不會通知任何回調。

注意:characteristic可能只支持特定的寫類型,或不支持寫操作。你可以通過檢查properties屬性是否包含CBCharacteristicPropertyWriteWithoutResponseCBCharacteristicPropertyWrite 來判斷。

三、peripheral角色的實現(xiàn)
3.1 peripheral角色的實現(xiàn)

接下來,你將學習如何使用peripheral。

  • 1,創(chuàng)建peripheral manager 對象
  • 2,構建servicescharacteristics
  • 3,發(fā)布servicescharacteristics到數(shù)據(jù)庫
  • 4,廣播你的服務
  • 5,響應讀和寫請求
  • 6,發(fā)送數(shù)據(jù)給centrals訂閱者
3.1.1 創(chuàng)建外設管理器
myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];

方法說明:創(chuàng)建的時候,peripheral manager將調用代理對象的peripheralManagerDidUpdateState:方法。

3.1.2 構建服務和特征值

servicescharacteristic是樹形結構組織的,服務和特征值使用uuid標識。

CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];
  • 1、為servicescharacteristics創(chuàng)建你自己的UUID
CBUUID *myCustomServiceUUID = [CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"];
  • 2、構建servicescharacteritics
myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID properties:CBCharacteristicPropertyRead value:myValue permissions:CBAttributePermissionsReadable];

propetiespermissions的設置決定了這個characteristic是否可讀,是否可寫,是否可訂閱。上例中我們把它設置成可讀。

注意:如果你設置value的值,這個值將被緩存,并且propertiespermissions將是只讀的。因此,如果你希望value是可寫的,或value值根據(jù)具體情況呈不同的值時,你必須把它設置成nil。這樣才能使它被動態(tài)賦值。才能響應peripheral manager的請求。
補充:

  • 1.針對CBMutableCharacteristicproperties屬性的說明:
CBCharacteristicPropertyBroadcast: 允許一個廣播特性值,用于描述特性配置,不允許本地特性
CBCharacteristicPropertyRead: 允許讀一個特性值
CBCharacteristicPropertyWriteWithoutResponse: 允許寫一個特性值,沒有反饋
CBCharacteristicPropertyWrite: 允許寫一個特性值
CBCharacteristicPropertyNotify: 允許通知一個特性值,沒有反饋
CBCharacteristicPropertyIndicate: 允許標識一個特性值
CBCharacteristicPropertyAuthenticatedSignedWrites: 允許簽名一個可寫的特性值
CBCharacteristicPropertyExtendedProperties: 如果設置后,附加特性屬性為一個擴展的屬性說明,不允許本地特性
CBCharacteristicPropertyNotifyEncryptionRequired: 如果設置后,僅允許信任的設備可以打開通知特性值
CBCharacteristicPropertyIndicateEncryptionRequired: 如果設置后,僅允許信任的設備可以打開標識特性值

  • 2.針對CBMutableCharacteristicpermissions權限的說明:
CBAttributePermissionsReadable // 讀
CBAttributePermissionsWriteable // 寫
CBAttributePermissionsReadEncryptionRequired //需要連接成功后,允許讀(建議:安全數(shù)據(jù)這么做)
CBAttributePermissionsWriteEncryptionRequired //需要連接成功后,允許寫(建議:安全數(shù)據(jù)這么做)

  • 2.1 創(chuàng)建service
myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];

說明:這里的第二個參數(shù)設置為YES,標明這個服務值主要的。主要服務體現(xiàn)了主要的功能,并能夠被其它服務引用。次要服務只用來描述其引用的服務的相關的信息。比如,心率監(jiān)控的主要服務用來顯示心率數(shù)據(jù),這時次要服務可能就用來顯示電池數(shù)據(jù)。

  • 2.2 關聯(lián)characteristics
myService.characteristics = @[myCharacteristic];
  • 3 發(fā)布服務和特征值
[myPeripheralManager addService:myService];

這里會觸發(fā)代理消息

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
    if (error){
        NSLog(@"Error publishing service: %@", [error localizedDescription]);
    }
}

如果有異常,請通過error查詢。
注意:服務和特征值一旦發(fā)布,不能更改。

  • 4 廣播你的服務
[myPeripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey :@[myFirstService.UUID, mySecondService.UUID] }];

例子中參數(shù)是dictionary,不是array。并且只有一個key。在這里只支持兩個key,CBAdvertisementDataLocalNameKeyCBAdvertisementDataServiceUUIDsKey。詳情參見Advertisement Data Retrieval Keys in CBCentralManagerDelegate Protocol Reference。
當你開始廣播你的服務,peripheral manager就會通知代理peripheralManagerDidStartAdvertising:error:
。如果有異常將不會發(fā)出廣播,并在代理中可查到異常的原因:

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
if (error) {
NSLog(@"Error advertising: %@", [error localizedDescription]);
  }
}

當你發(fā)出廣播后,遠程的centrals將可發(fā)現(xiàn)并初始化來取得連接。

  • 5 相應centrals的讀寫請求
    在連接到遠程的centrals之后,你就可以開始接收讀或寫請求。

  • 5.1 讀請求
    central有讀請求時,peripheral manager調用 peripheralManager:didReceiveReadRequest:代理方法。并把請求信息通過CBATTRequest傳過來。
    比如當你收到一個讀請求時,CBATTRequest對象的屬性可以確保設備的數(shù)據(jù)庫中的特征值能匹配central所標記的那個特征值。代理的方法實現(xiàn)類似如下:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
 ...
}

如果特征值的UUID匹配,下一步就是確保請求所讀的數(shù)據(jù)不越界。

if (request.offset > myCharacteristic.value.length) {
    [myPeripheralManager respondToRequest:request
    withResult:CBATTErrorInvalidOffset];
    return;
}

如果請求的偏移量沒有越界,那么設置請求的值。

request.value = [myCharacteristic.value subdataWithRange:NSMakeRange(request.offset,myCharacteristic.value.length - request.offset)];

設置好返回值(request.value)之后,使用respondToRequest:withResult:方法來返回數(shù)據(jù),類似如下:

[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];

每次收到的請求peripheralManager:didReceiveReadRequest: 都應該有相應的返回respondToRequest:withResult:方法。
注意:假如特征值的UUID不匹配,或是讀請求由于某些原因無法完成。你也應該調用respondToRequest:withResult:方法來返回失敗結果。失敗結果列表參見CBATTError Constants enumeration in Core Bluetooth Constants Reference。

  • 5.2 寫請求
    寫請求的處理也很簡單。當central發(fā)送請求要求寫數(shù)據(jù)時,peripheral manager調用peripheralManager:didReceiveWriteRequests:代理方法,數(shù)據(jù)通過參數(shù)CBATTRequest傳遞給你。
    比如:當你收到一個寫請求時,CBATTRequest對象的屬性可以確保設備的數(shù)據(jù)庫中的特征值能匹配central所標記的那個特征值。代理的方法實現(xiàn)類似如下:
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didReceiveWriteRequests:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
 ...
}

如果特征值的UUID匹配,下一步就是確保請求所讀的數(shù)據(jù)不越界。

if (request.offset > myCharacteristic.value.length) {
    [myPeripheralManager respondToRequest:request
    withResult:CBATTErrorInvalidOffset];
    return;
}

如果請求的偏移量沒有越界,那么設置請求的值。

myCharacteristic.value = request.value;

這里同樣需要注意請求的偏移量問題。
跟讀請求相似,每次都要調用respondToRequest:withResult:方法來回應消息。也就是說,雖然代理中入?yún)⑹?code>array,可能包含多個CBATTRequest,但是回應時是單個的CBATTRequest對象。你必須傳入array中的第一個對象。如下:

[myPeripheralManager respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess];

注意:把多個請求看成一個對待,如果其中有一個請求無法實現(xiàn),那么所有的請求就將無法實現(xiàn)。同時,調用respondToRequest:withResult:方法回應,并提供失敗的原因。

  • 6 訂閱Characteristic Values
    通常,centrals可以訂閱一個或多個特征值,這在Subscribing to a Characteristic’s Value. 中也有描述。這種情況下如果他們訂閱的特征值的值有變化,你應該要能夠給他們發(fā)消息。
  • 6.1 調用代理方法
    當一個central訂閱某個特征值,peripheral manager將通知代理peripheralManager:central:didSubscribeToCharacteristic:方法。
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"Central subscribed to characteristic %@", characteristic);
}
  • 6.2 獲取更新的值并發(fā)送給central
NSData *updatedValue = // fetch the characteristic's new value
BOOL didSendValue = [myPeripheralManager updateValue:updatedValue forCharacteristic:characteristic onSubscribedCentrals:nil];

在你調用這個方法來發(fā)送數(shù)據(jù)給訂閱者時,你可以在最后一個參數(shù)標明哪個central,如上。如果你寫nil,所有連接著的有訂閱的centrals都將收到信息,當然有連接但沒訂閱的會被忽略。

updateValue:forCharacteristic:onSubscribedCentrals: 這個方法返回Boolean值,指明數(shù)據(jù)是否成功發(fā)送。如果底層的隊列正在傳輸數(shù)據(jù),這個方法就會返回NO。當傳輸隊列重新變?yōu)榭臻e時,則會調用peripheral manager的代理方法peripheralManagerIsReadyToUpdateSubscribers:,這時你就可以利用這個代理重新發(fā)送數(shù)據(jù),而不需要重新調用updateValue:forCharacteristic:onSubscribedCentrals:方法。

注意:使用通知來發(fā)送一個數(shù)據(jù)包給central訂閱者。也就是說,當你需要給訂閱者更新數(shù)據(jù)時,你應該在通知中發(fā)送整個數(shù)據(jù),通過一次調用updateValue:forCharacteristic:onSubscribedCentrals:方法來實現(xiàn)。局限于特征值數(shù)據(jù)大小的限制,并不是所有數(shù)據(jù)都能用通知來傳遞。這種情況下,應該由central端通過調用CBPeripheralreadValueForCharacteristic: 方法來獲取整個數(shù)據(jù)。

四、iOS藍牙應用的后臺處理
4.1 iOS藍牙應用的后臺處理

對于iOS應用,你必須要清楚它是在前臺運行,還是在后臺運行。因為資源有限,你要對這兩種模式區(qū)別處理。
默認情況下,當應用進入后臺或掛起時,藍牙任務是不執(zhí)行的。但是,你可以把應用聲明為支持藍牙后臺執(zhí)行模式,這樣當有藍牙相關事件發(fā)生時,你的應用就可以被喚醒來處理任務。即使你的應用不要求后臺處理支持,當有重要的事件發(fā)生時,系統(tǒng)仍然可能跳出警告,要求處理。

即使你的應用支持一種或兩種都支持后臺執(zhí)行模式,也不是就一定能永遠執(zhí)行。在某些情況下,系統(tǒng)可能終止你的應用以便為前臺應用讓出內存,這將導致當前活動和連接等信息丟失。自iOS7之后,藍牙庫支持保存狀態(tài)信息,并可在下次啟動app時還原狀態(tài)信息。你可以通過這個特性來實現(xiàn)長連接。

4.1.1 只支持前臺運行的應用

大部分的apps,除非你要求后臺運行,在進入后臺后,應用會很快被掛起。在掛起狀態(tài)下,應用無法處理藍牙相關任務,無法接收藍牙事件。直到重新回到前臺。

central端,只支持前臺運行的應用,在進入后臺或被掛起時就無法掃描和發(fā)現(xiàn)peripheral的廣播包。如果是在peripheral端,廣播將停止,任何central想訪問characteristic的值都將收到異常信息。

不同情況下,默認的行為可能會影響你的程序。比如,在你與peripheral交互數(shù)據(jù)時,應用掛起(比如用戶切到另一個應用)。這時連接可能會斷開,你并不會收到通知,直到應用重新激活。

####### 4.1.1.1 利用peripheral連接選項
只支持前臺的藍牙應用在掛起后發(fā)生的藍牙事件會被系統(tǒng)排隊,并在應用進入前臺時把事件發(fā)給應用。當特定的central事件發(fā)生時,藍牙庫可以提供一種方式來提示用戶。用戶可以根據(jù)這些提示來決定是否激活應用。

若想利用使用這些提示,你需要在調用connectPeripheral:options: 方法時傳入如下參數(shù)。
CBConnectPeripheralOptionNotifyOnConnectionKey: 在應用掛起后,與指定的peripheral成功建立連接,則發(fā)出通知
CBConnectPeripheralOptionNotifyOnDisconnectionKey: 在應用掛起后,如果與指定的peripheral斷開連接,則發(fā)出通知
CBConnectPeripheralOptionNotifyOnNotificationKey: 在應用掛起后,指定的peripheral有任何通知都進行提示

####### 4.1.1.2 后臺執(zhí)行模式

如果你的應用在后臺時也需要處理藍牙事件,就必須在Info.plist中聲明應用要支持藍牙后臺模式,這樣,當有藍牙事件發(fā)生時,系統(tǒng)會喚醒應用來處理。

有兩種藍牙后臺模式,一種為central角色,另一種為peripheral角色。如果應用需要兩種角色,則可以聲明支持兩種模式。聲明方式:增加UIBackgroundModes 鍵,并增加包含下列字符串的array值。

? bluetooth-central —The app communicates with Bluetooth low energy peripherals using the Core Bluetooth framework. 
? bluetooth-peripheral —The app shares data using the Core Bluetooth framework

注意:Info.plist中會顯示為更加人性化的文本,不是直接顯示實際的鍵值對。如要顯示實際值,可右鍵,或control點擊,在彈出菜單中選擇Open As -> Source Code

 <key>UIBackgroundModes</key>
 <array>
 <string>bluetooth-central</string>
 <string>bluetooth-peripheral</string>
 </array>

或者(推薦使用這個操作)


設置后臺模式.png

對后臺模式的說明:

  • 支持central后臺運行的模式
    如果你的應用支持central角色的后臺模式,也就是Info.plistUIBackgroundModes鍵的值中包含bluetooth-central值。那么應用將可以在后臺處理特定的藍牙相關事件。即使在后臺,你仍然可以發(fā)現(xiàn)和連接peripherals,可以檢索和讀寫數(shù)據(jù)。并且當有CBCentralManagerDelegate or CBPeripheralDelegate 代理事件發(fā)生時,系統(tǒng)會喚醒應用來處理。
    需要注意的是,進入后臺時,掃描的處理有些區(qū)別:
  • 1, CBCentralManagerScanOptionAllowDuplicatesKey這個鍵會被忽略,多次發(fā)現(xiàn)同一peripheral會被合并成一個發(fā)現(xiàn)事件。
  • 2,如果所有掃描中的應用都在后臺,那么你應用的掃描間隙會延長。結果是,掃描到peripheral的時間可能會延長。這樣做是為了減少輻射節(jié)省電量。
  • 支持peripheral后臺運行的模式
    如果要支持peripheral角色的后臺模式,你需要在Info.plist中的增加UIBackgroundModes鍵并在值中包含bluetooth-peripheral值。這樣系統(tǒng)會喚醒應用來處理讀寫和訂閱事件。
    藍牙框架(Core Bluetooth framework)不僅允許你的應用被喚醒來處理讀寫和訂閱請求,還允許你的應用在后臺狀態(tài)下發(fā)送廣播。但你必須注意后臺時廣播與前臺時廣播是不同的。即便如此,你必須注意后臺與前臺時廣播處理的區(qū)別。特別是當你的應用需要在后臺發(fā)送廣播。
  • 1,CBAdvertisementDataLocalNameKey 這個鍵會被忽略,并且peripherallocal name不會被廣播
  • 2,CBAdvertisementDataServiceUUIDsKey 的值中包含的所有service uuids都會被放到“overflow”區(qū)域;只有iOS設備顯示指明在搜索它時才會搜索到這些值。
  • 3,如果所有的處于廣播狀態(tài)的應用都在后臺,廣播頻率將降低。

####### 4.1.1.3 明智使用后臺運行模式
雖然為了完成某些事情,有必要把你的應用聲明成支持后臺運行模式,你也應該要能有效處理后臺任務。因為執(zhí)行藍牙任務會使用無線電,從而耗費電池電量,所以盡量最小化后臺任務。應用被藍牙事件喚醒后應能盡快處理好任務,以便被重新掛起。
支持后臺運行的任務要遵循幾個原則

  • 1,應用應該是基于會話的,并提供接口讓用戶決定何時開始或停止藍牙事件。
  • 2,應用被喚醒后,大約有10秒鐘的時間來完成任務,所以應該盡快完成任務并重新掛起。若在后臺花費太多時間,則將受到系統(tǒng)的遏制甚至被扼殺。
  • 3,應用不應該使用這種被喚醒的機會來執(zhí)行與之無關的事情。

####### 4.1.1.4 后臺長時間執(zhí)行
一些應用需要長時間后臺運行。舉個例子,你可發(fā)一款家庭安全應用,iOS設備與藍牙門鎖通訊。當用戶離開家時自動鎖門,當用戶回到家時門自動打開,整個過程應用都是后臺運行。當用戶離開家時,iOS設備與門鎖斷開連接。這是應用只簡單調用connectPeripheral:options:,因為連接沒有超時,iOS設備將在用戶回到家時重新連接上。
假設用戶離開家好幾天,并假設app被系統(tǒng)終止,應用將無法在用戶回到家時重連門鎖,這時用戶將無法開門。對于這類應用,很重要的一點要能夠繼續(xù)使用藍牙執(zhí)行長時事件,如管理活動和懸停連接。

  • 1、狀態(tài)保存和還原
    因為狀態(tài)保存和還原是藍牙內在支持的,你的應用可選擇支持這一特征來讓系統(tǒng)保存centralperipheral manager的狀態(tài),并繼續(xù)執(zhí)行藍牙任務,即使你的應用不在運行。當任務完成,系統(tǒng)重新激活應用到后臺,讓你的應用有機會還原狀態(tài)并處理事件。上面說的家庭安全應用,系統(tǒng)可以管理連接請求,并在用戶回到家重新連接上藍牙時重新激活應用來處理 centralManager:didConnectPeripheral: 代理回調。

藍牙庫支持狀態(tài)保存和還原,支持central角色,peripheral角色。
當應用實現(xiàn)central角色并增加支持狀態(tài)保存和還原,系統(tǒng)就會在終止應用釋放內存前保存central manager對象的狀態(tài),如果應用有多個central managers,你可選擇哪些對象你希望系統(tǒng)為你維護。對于CBcentralManger對象,系統(tǒng)如此維護:

  • 1,central manager掃描的services和對應的options
  • 2,已連接的和未連接上的peripherals
  • 3,訂閱的characteristics

當應用實現(xiàn)peirpheral角色的應用類似處理。對于CBPeripheralManager對象,系統(tǒng)這樣維護:

  • 1,廣播的數(shù)據(jù)
  • 2,peripheral manager發(fā)布到設備數(shù)據(jù)庫的servicescharacteristic
  • 3,那些訂閱了你characteristics的值得centrals

當應用被系統(tǒng)重新激活到后臺,假如應用之前有發(fā)現(xiàn)peripheral,你可以重新創(chuàng)建應用的centralperipheral manager,并還原他們的狀態(tài)。后面將繼續(xù)說明如何利用狀態(tài)保存與還原。

  • 2 增加支持狀態(tài)保存與還原
    這一特性是可選的,增加步驟如下:
  • 1,(必須)在創(chuàng)建和初始化時選擇支持狀態(tài)保存和還原。Opt In to State Preservation and Restoration 這一節(jié)將更詳細描述
  • 2,(必須)在應用被系統(tǒng)喚醒時復原centralperipheral manager對象。Reinstantiate Your Central and Peripheral Managers 這里將繼續(xù)描述
  • 3,(必須)實現(xiàn)還原代理方法。Implement the Appropriate Restoration Delegate Method。 這里將繼續(xù)說明
  • 4,(可選)更新centralperipheral managers的初始化過程。Update Your Initialization Process。這里將繼續(xù)說明
  • 2.1 Opt In to State Preservation and Restoration(選擇支持狀態(tài)保存和還原)
    在創(chuàng)建和初始化時,提供唯一的還原id。還原id是字符串,對于藍牙庫和應用來說,還原id是用來標記centralperipheral manger的。你的代碼只關心這個字符串,但這個字符串告訴藍牙庫需要保存被標記對象的狀態(tài)。藍牙庫只保存那些有標記還原id的對象的狀態(tài)。

假如,選擇支持狀態(tài)保存和還原的應用只有一個CBCentralMnager對象實例實現(xiàn)了central角色,那么在初始化時初始化options中增加CBCentralManagerOptionRestoreIdentifierKey 鍵,并賦值還原id

myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{ CBCentralManagerOptionRestoreIdentifierKey: @"myCentralManagerIdentifier" }];

peripheral manager的處理也是類似的,keyCBPeripheralManagerOptionRestoreIdentifierKey

注意:因為應用可以有多個CBCentralManagerCBPeripheralManger實例。注意每個還原id都是唯一的,這樣系統(tǒng)才能區(qū)分開來。

  • 2.2 Reinstantiate Your Central and Peripheral Managers(復原centralperipheral manager)

當應用被系統(tǒng)喚醒,你需要做的第一件事是使用還原id復原central and peripheral manager。如果應用中只有一個central or peripheral manager,并且在應用的整個生命周期中存在,那么就簡單了。

如果應用使用多個central or peripheral manager或如果應用使用的manager不是在app的整個生命周期中存在,那么應用需要知道哪些managers需要復原。在實現(xiàn)application:didFinishLaunchingWithOptions: 這個代理方法時,通過使用參數(shù)launchoptions中的鍵(UIApplicationLaunchOptionsBluetoothCentralsKey or UIApplicationLaunchOptionsBluetoothPeripheralsKey)可以獲得應用在終止時為我們保存的還原id列表。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
 NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
 ...
}

有了還原id列表后,就可以復原出central manager對象了。
注意:當應用被激活時,系統(tǒng)只提供那些應用終止時有藍牙任務的central and peripheral managers的還原ids。

  • 2.3 Implement the Appropriate Restoration Delegate Method(實現(xiàn)還原代理方法)
    在重新創(chuàng)建central and peripheral managers之后,需要通過藍牙系統(tǒng)還原他們的狀態(tài)。對于central managers,要實現(xiàn)centralManager:willRestoreState: 代理方法,對于peripheral managers實現(xiàn)peripheralManager:willRestoreState:方法。

重要:對于使用狀態(tài)保存和還原特性的應用,應用被激活到后臺的第一個代理調用是centralManager:willRestoreState: and peripheralManager:willRestoreState:。對于未使用這一特性的應用,第一個代理調用是centralManagerDidUpdateState: and peripheralManagerDidUpdateState:

// 應用從后臺恢復到前臺的時候,會和系統(tǒng)藍牙進行同步,調用此方法
 - (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict{
    NSArray *peripherals = state[CBCentralManagerRestoredStatePeripheralsKey];
// 如果數(shù)組不為空,這里直接設置代理,發(fā)現(xiàn)服務
if(peripherals.count != 0){
    CPeripheral *peripheral = [peripherals firstObject];
    Self.peripheral = peripheral; //強引用一下
    Peripheral.delegate = self; // 設置代理
    [peripheral discoverServices:@[]];// 服務數(shù)組
  }
}

在這些代理中,最后一個參數(shù)是dictionary,包含了應用被終止時managers的信息??捎面I值參考 Central Manager State Restoration Options constants in CBCentralManagerDelegate Protocol Reference and the Peripheral_Manager_State_Restoration_Options constants in CBPeripheralManagerDelegate Protocol Reference

CBCentralManagerRestoredStatePeripheralsKey // 返回一個中心設備正在連接的所有外設數(shù)組 
CBCentralManagerRestoredStateScanServicesKey // 返回一個中心設備正在掃描的所有服務UUID的數(shù)組 
CBCentralManagerRestoredStateScanOptionsKey // 返回一個字典包含正在被使用的外設的掃描選項

要還原CBCentralMnager 對象的狀態(tài),要使用centralManager:willRestoreState: 方法中dictionary的鍵值對。舉個例子,假如centralmanger對象在app被終止時有acitvepending連接,系統(tǒng)會繼續(xù)管理他們。就像下面代碼所示,可以使用CBCentralManagerRestoredStatePeripheralsKey 鍵從dictionary中獲取所有設備的列表,這些設備就是central manger已連接或正在連接的設備。

如何使用這個列表要看具體情況。比如,如果應用要維護central manger已發(fā)現(xiàn)peripherals的列表,你可能就需要利用到它。參見Connecting to a Peripheral Device After You’ve Discovered It, 請注意在需要給peripheral設置相應的代理。

對于CBPeripheralManager對象,也需要類似的處理,相應的代理方法是peripheralManager:willRestoreState:

  • 2.4 Update Your Initialization Process(更新你的初始化進程)

在前面的三個步驟之后,你可能想知道central and peripheral manager的初始化進程。雖然這是一個可選步驟,但如果想讓你的應用跑起來更流暢,這可是很重要的。假設應用在檢索peripheral的服務時被終止。當應用還原后,它不知道這個過程到底進行到哪一步了。你也想知道從哪一步繼續(xù)。
舉例,當在centralManagerDidUpdateState:方法中初始化你的應用時,你可以查到在應用被終止時你是否成功發(fā)現(xiàn)被還原peripheral的某個service,如下:

NSUInteger serviceUUIDIndex = [peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj,NSUInteger index, BOOL *stop) {
    return [obj.UUID isEqual:myService];
}];

if (serviceUUIDIndex == NSNotFound) {
    [peripheral discoverServices:@[myService]];
    ...
}else { // 應用在被終止前已搜索到`service`,那么你需要檢查時候搜索到你要的`characteristics`
}

如上,如果系統(tǒng)在應用發(fā)現(xiàn)service之前終止它,那么開始搜索peripheral的數(shù)據(jù),使用discoverServices:搜索。如果應用在被終止前已搜索到service,那么你需要檢查時候搜索到你要的characteristics,(如果有訂閱,也檢查是否已訂閱)。通過檢查初始化過程,可以確保在這時調用到最合適的方法。

五、與peripheral通信的最佳實踐
5.1 與peripheral通信的最佳實踐

藍牙庫使得central端的一些事情的處理變得透明。也就是你的應用可以實現(xiàn)central端的大部分事情,如搜索和連接設備,檢索和交互peripheral的數(shù)據(jù)。這一章將提供開發(fā)指引及如何最佳實現(xiàn)。

5.2 永遠記住需要使用無線電并且會消耗電量

當開發(fā)應用需要用到藍牙低功耗設備,請記住藍牙低功耗通訊需要使用你設備的無線電來傳輸信號。同時其他形式的無線通訊也可能使用設備的無線電—比如,wi-fi,經(jīng)典藍牙,甚至其他使用藍牙低功耗的應用—所以,盡量讓你的應用盡少使用無線電。

在開發(fā)iOS應用中,盡少使用無線電是非常重要的,因為無線電的使用會減少電池的壽命。下面的指引將幫助你如何以更好的方式使用無線電,從而使你的應用表現(xiàn)更佳,更省電。
####### 5.2.1 只有在需要的時候才搜索設備
當你調用CBCentralManagerscanForPeripheralsWithServices:options: 方法來搜索(發(fā)現(xiàn))當前正在發(fā)送廣播的peripheral,這時你正在使用你設備的無線電來監(jiān)聽廣播設備,直到你顯示的停止它。
除非你需要搜索更多的設備,否則在你搜索到所需要的設備后,及時的停止搜索。使用CBCentralManagerstopScan方法停止搜索設備,參見Connecting to a Peripheral Device After You’ve Discovered It.

####### 5.2.2 只有在需要的時候才添加CBCentralManagerScanOptionAllowDuplicatesKey搜索參數(shù)

peripheral設備一秒鐘可能發(fā)送多個廣播包。在搜索peripheral時,默認情況下,多次搜到的廣播包只會觸發(fā)一次事件代理。結果就是不管收到多少個廣播包,central manager對一個peripheral只會觸發(fā)centralManager:didDiscoverPeripheral:advertisementData:RSSI: 方法一次。但是如果peripheral的廣播數(shù)據(jù)變了,也會重新觸發(fā)代理事件。

如果你不喜歡這種默認的方式,你可以在搜索時給搜索方法scanForPeripheralsWithServices:options:添加CBCentralManagerScanOptionAllowDuplicatesKey參數(shù)。 這樣的話,就可以每次收到廣播包信息都觸發(fā)消息。在某些情況下,我們需要這么做。比如說需要檢查設備是在靠近還是在遠離(通過RSSI值判斷)。所以,請記住添加這個參數(shù)會消耗電量,并影響性能,只有在確定需要的時候才使用這個參數(shù)。

####### 5.2.3 明智的檢索peripheral的數(shù)據(jù)
peripheral設備可能會有多個servicescharacteristics。如果搜索全部的servicescharacteristics會浪費電量,對應用性能也不好。所以我們應該只搜索那些我們需要的servicescharacteristics。

[peripheral discoverServices:@[firstServiceUUID, secondServiceUUID]];

在搜索到需要的services之后,接著就搜索需要的characteristics。同樣的,使用CBUUID封裝好需要的characteristics,使用CBPeripheraldiscoverCharacteristics:forService:方法開始搜索。

####### 5.2.4 訂閱經(jīng)常改變的characteristic
Retrieving the Value of a Characteristic談過,有兩種方式可以獲得characteristic的值。

  • 1,在需要的時候使用readValueForCharacteristic: 輪詢characteristic的值
  • 2,使用setNotifyValue:forCharacteristic: 訂閱這個characteristic。

可能的話,最好采用訂閱的方式,特別是在characteristic的值經(jīng)常變的情況下。至于如何訂閱參見Subscribing to a Characteristic’s Value.

####### 5.2.5 當獲得全部所需數(shù)據(jù)后斷開設備連接
當連接已不再需要的時候,斷開連接,這樣有助于降低無線電的使用。下面兩種情況,你應該斷開與peripheral的連接。

  • 1,你所訂閱的特征值已不再發(fā)通知。(可以通過characteristic的isNotifying屬性得知
  • 2,你已獲得了全部所需要的數(shù)據(jù)。

這兩種情況下,取消訂閱并斷開連接。通過setNotifyValue: forCharacteristic:設置第一個參數(shù) 為NO來取消訂閱,通過CBCentralManager 的cancelPeripheralConnection:方法取消連接。如下:
取消訂閱

 [self.peripheral setNotifyValue:YES forCharacteristic:characteristic];  // NO 取消訂閱

斷開連接

[myCentralManager cancelPeripheralConnection:peripheral];

注意cancelPeripheralConnection: 這個方法是非阻塞的,所以其他正在進行的通訊可能完成也可能因此沒完成。因為其他應用可能仍保持跟這個設備的連接,所以當前的取消連接并不能保證底層就立即斷開物理上的連接。但從應用本身看來,這個設備已經(jīng)被認為是斷開連接了,central manager對象也對觸發(fā)代理的centralManager:didDisconnectPeripheral:error: 方法。

5.3 重新連接peripherals

有三種方式可以重連

  • 1,重新獲取已發(fā)現(xiàn)的設備列表(搜索到的或是連接過的設備),使用 retrievePeripheralsWithIdentifiers: 。如果列表中有想要尋找的設備,那么發(fā)起連接。參見Retrieving a List of Known Peripherals
  • 2,重新獲取當前連接著的設備列表,使用retrieveConnectedPeripheralsWithServices:。 如果列表中有想要尋找的設備,發(fā)起本地連接,使得應用與之連接上。參見Retrieving a List of Connected Peripherals
  • 3,使用scanForPeripheralsWithServices:options:重新搜索設備,如果找到了就去連接。參見Discovering Peripheral Devices That Are Advertising and Connecting to a Peripheral Device After You’ve Discovered It.

實際情況下,你可能不想每次連接都先去搜索設備,你可能更希望使用另外兩種方式。如圖所示,一種先嘗試使用另兩種方式連接的方法

一種先嘗試使用另兩種方式連接的方法 .png

注意:采用什么方式,什么順序依情況而定,比如你可以不采用第一種方式,也可以同時使用前兩種并行的方式。

####### 5.3.1 Retrieving a List of Known Peripherals(獲取已知設備列表)
當你第一次發(fā)現(xiàn)某個peripheral,系統(tǒng)會為他生成一個identifier(一個NSUUID對象),你可以保存這個identifier(比如用NSUserDefaults保存),然后在后面需要用的時候使用retrievePeripheralsWithIdentifiers:重新連接。

knownPeripherals = [myCentralManager retrievePeripheralsWithIdentifiers:savedIdentifiers];

central manager嘗試匹配你傳入的identifiers,并返回CBPeripheral對象。如果沒有匹配的設備,array將為空,這時你需要使用另外兩種方式。如果不為空,那么讓用戶選擇要連接到哪個設備。

當用戶選擇了要連接的設備,則調用connectPeripheral:options:方法來嘗試連接。如果設備被連接上,則會觸發(fā)代理消息centralManager:didConnectPeripheral:。

注意:可能有多種原因導致設備不能被連接上。比如,設備不在附近。還有一種可能,一些低功耗藍牙使用隨機設備地址,在重新連接時,它的地址可能已經(jīng)變了。因此,即使設備就在附近,設備的地址也已經(jīng)變了,這種情況下,你想要連接的設備與實際設備已經(jīng)不匹配了。這種情況,你只有重新搜索了。更多詳情請參考Bluetooth 4.0 specification, Volume 3, Part C, Section 10.8 and Bluetooth Accessory Design Guidelines for Apple Products.

####### 5.3.2 Retrieving a List of Connected Peripherals(獲取已連接藍牙)

另一種重新連接的方法是檢查你想要連接的設備是否已經(jīng)連接到系統(tǒng)了(可能其他應用正連著呢)。你可以使用retrieveConnectedPeripheralsWithServices:方法獲取CBPeripheral對象Array。

因為當前可能有多個peripheral連接著系統(tǒng),你可以傳遞CBUUID對象(注意是serviceUUID)的Array。這樣他將只返回當前連接著的,并且包含array中所有serivesperipheral。如果沒有符合條件的,則返回的array為空,這時你需要使用別的方法。如果array不為空,那么讓用戶來選擇。

如果用戶找到并選擇了所要的peripheral,使用connectPeripheral:options:把它連接到你的應用。當連接建立,會觸發(fā)代理centralManager:didConnectPeripheral:這時說明連接成功了。

六、作為Peripheral端的最佳方式

作為Peripheral端的最佳方式
central端類似,藍牙庫讓你能夠實現(xiàn)peripheral角色的多方面的控制。本章節(jié)提供指引,并講述如何使用。

6.1 廣播是實現(xiàn)peripheral建立連接的一個重要部分。

####### 6.1.1 注意廣播數(shù)據(jù)的限制

CBPeripheralManagerstartAdvertising:方法中,通過dictionary參數(shù)傳遞peripheral的廣播數(shù)據(jù)。創(chuàng)建廣播字典時,時刻記住有哪些限制。

  • 1.雖然廣播包通常情況下可以有多種的信息,但你只能廣播設備名稱和servicesuuid。也就是說,在你創(chuàng)建廣播字典時,你只能設置這兩個key: CBAdvertisementDataLocalNameKey and CBAdvertisementDataServiceUUIDsKey 。設置其他key將導致錯誤。
  • 2.廣播數(shù)據(jù)的空間也有限制。當應用在前臺時,這兩個key的值最多有28 字節(jié)可用。搜索時,如果這個空間用完,另外還有10個字節(jié)的響應數(shù)據(jù)可以用來標識設備名稱。超出規(guī)定空間的service uuids將會被放到“Overflow”區(qū)域,在iOS設備顯示搜索他們時會被搜索到。當設備在后臺時,local name不會被廣播,同時所有的services uuids都放入 overflow 區(qū)域。

注意:這個限制的數(shù)值不包括2字節(jié)的頭部信息。詳細格式信息參見Bluetooth 4.0 specification, Volume 3, Part C, Section 11.

為了使你的數(shù)據(jù)符合空間要求,請使用主要的services的uuid。

####### 6.1.2 只在需要的時候廣播數(shù)據(jù)
廣播數(shù)據(jù)會使用設備的無線電(當然了還有電池),所以只有在你希望被連的時候才廣播數(shù)據(jù)。一旦連接上,這個設備就可以檢索和交互數(shù)據(jù)了,而不需要你在廣播數(shù)據(jù)了。因此,為了少使用無線電,增加應用響應性能,保護電池,請停止發(fā)送廣播。使用stopAdvertising方法停止,如下

[myPeripheralManager stopAdvertising];

####### 6.1.3 讓用戶決定什么時候廣播
往往只有用戶才最知道什么時候需要發(fā)送廣播。如果附近沒有藍牙設備,你發(fā)送廣播也是沒有意義的。既然應用本身不知道何時需要發(fā)送廣播,那就提供接口讓用戶來決定。

6.2 配置characteristic

創(chuàng)建可變characteristic之后,我們修改其屬性,值,和訪問權限。這些設置決定連接的central如何訪問和與之交互數(shù)據(jù)。雖然不同的屬性和訪問權限由各自app決定,但當你要完成下面兩項任務時,這里提供相關指引:

  • 1,允許central訂閱你的characteristics
  • 2,如果未配對,請保護敏感數(shù)據(jù)
6.3 設置characteristic,使之支持通知

Subscribe to Characteristic Values That Change Often中有描述,推薦在characteristic的值經(jīng)常變化時,使用訂閱的方式。如果可能的話,盡量讓central端能夠采用訂閱的方式。

創(chuàng)建的時候,通過CBCharacteristicPropertyNotify設置characteristic的屬性,使之支持訂閱。

myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];

這樣,characteristic會是可讀,可被訂閱的。

6.4 要求配對連接才能訪問敏感數(shù)據(jù)

實際情況下,你可能需要發(fā)送敏感數(shù)據(jù)。比如數(shù)據(jù)中有姓名,email地址等,你希望只有可信任的設備才能訪問這些數(shù)據(jù)。

要保證只有可信任的設備才能訪問敏感數(shù)據(jù),可以通過設置合適的characteristic屬性和訪問權限。比如上面創(chuàng)建的例子,可做如下修改:

emailCharacteristic = [[CBMutableCharacteristic alloc] initWithType:emailCharacteristicUUID properties:CBCharacteristicPropertyRead| CBCharacteristicPropertyNotifyEncryptionRequired value:nil permissions:CBAttributePermissionsReadEncryptionRequired];

在這里characteristic被配置成只允許可信任設備訪問和訂閱他的值。當central想要連接和訂閱這個characteristic的值時,藍牙庫就會嘗試配對來建立安全連接。

配對過程完成后,peripheral就會認為這個central是一個可信任設備,并允許central訪問其加密過的值。

參考文獻
參考文獻

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • CoreBluetooth 簡介CoreBlueTooth.framework使用 CoreBluetooth 要...
    Jack_Baiyx閱讀 695評論 0 1
  • 藍牙是開發(fā)中常用的功能。通過手機APP與硬件設備連接,進而操作硬件,在開發(fā)中越來越常見。 藍牙的理論知識可自行從網(wǎng)...
    維斯特洛榮閱讀 459評論 0 0
  • 本文主要以藍牙4.0做介紹,因為現(xiàn)在iOS能用的藍牙也就是只僅僅4.0的設備 用的庫就是core bluetoot...
    暮雨飛煙閱讀 909評論 0 2
  • 藍牙簡介 藍牙( Bluetooth? ):是一種無線技術標準,可實現(xiàn)固定設備、移動設備和樓宇個人域網(wǎng)之間的短距離...
    Chefil閱讀 2,155評論 2 19
  • 前一段時間,我的朋友的女兒從美國上學回來,向我咨詢學國畫的事,她說以前對國畫并不感冒,現(xiàn)在卻發(fā)現(xiàn)國畫真是好東西!原...
    焦海洋漫畫閱讀 900評論 0 2

友情鏈接更多精彩內容