原文:http://blog.csdn.net/swibyn/article/details/52096646
Core Bluetooth Background Processing for iOS Apps
iOS藍(lán)牙應(yīng)用的后臺(tái)處理
對(duì)于ios應(yīng)用,你必須要清楚它是在前臺(tái)運(yùn)行,還是在后臺(tái)運(yùn)行。因?yàn)橘Y源有限,你要對(duì)這兩種模式區(qū)別處理。
默認(rèn)情況下,當(dāng)應(yīng)用進(jìn)入后臺(tái)或掛起時(shí),藍(lán)牙任務(wù)是不執(zhí)行的。但是,你可以把應(yīng)用聲明為支持藍(lán)牙后臺(tái)執(zhí)行模式,這樣當(dāng)有藍(lán)牙相關(guān)事件發(fā)生時(shí),你的應(yīng)用就可以被喚醒來處理任務(wù)。即使你的應(yīng)用不要求后臺(tái)處理支持,當(dāng)有重要的事件發(fā)生時(shí),系統(tǒng)仍然可能跳出警告,要求處理。
即使你的應(yīng)用支持一種或兩種都支持后臺(tái)執(zhí)行模式,也不是就一定能永遠(yuǎn)執(zhí)行。在某些情況下,系統(tǒng)可能終止你的應(yīng)用以便為前臺(tái)應(yīng)用讓出內(nèi)存,這將導(dǎo)致當(dāng)前活動(dòng)和連接等信息丟失。自ios7之后,藍(lán)牙庫支持保存狀態(tài)信息,并可在下次啟動(dòng)app時(shí)還原狀態(tài)信息。你可以通過這個(gè)特性來實(shí)現(xiàn)長連接。
Foreground-Only Apps
只支持前臺(tái)運(yùn)行的應(yīng)用
大部分的apps,除非你要求后臺(tái)運(yùn)行,在進(jìn)入后臺(tái)后,應(yīng)用會(huì)很快被掛起。在掛起狀態(tài)下,應(yīng)用無法處理藍(lán)牙相關(guān)任務(wù),無法接收藍(lán)牙事件。直到重新回到前臺(tái)。
在central端,只支持前臺(tái)運(yùn)行的應(yīng)用,在進(jìn)入后臺(tái)或被掛起時(shí)就無法掃描和發(fā)現(xiàn)peripheral的廣播包。如果是在peripheral端,廣播將停止,任何central想訪問characteristic的值都將收到異常信息。
不同情況下,默認(rèn)的行為可能會(huì)影響你的程序。比如,在你與peripheral交互數(shù)據(jù)時(shí),應(yīng)用掛起(比如用戶切到另一個(gè)應(yīng)用)。這時(shí)連接可能會(huì)斷開,你并不會(huì)收到通知,直到應(yīng)用重新激活。
Take Advantage of Peripheral Connection Options
利用peripheral連接選項(xiàng)
只支持前臺(tái)的藍(lán)牙應(yīng)用在掛起后發(fā)生的藍(lán)牙事件會(huì)被系統(tǒng)排隊(duì),并在應(yīng)用進(jìn)入前臺(tái)時(shí)把事件發(fā)給應(yīng)用。當(dāng)特定的central事件發(fā)生時(shí),藍(lán)牙庫可以提供一種方式來提示用戶。用戶可以根據(jù)這些提示來決定是否激活應(yīng)用。
若想利用使用這些提示,你需要在調(diào)用connectPeripheral:options: 方法時(shí)傳入如下參數(shù)。
CBConnectPeripheralOptionNotifyOnConnectionKey: 在應(yīng)用掛起后,與指定的peripheral成功建立連接,則發(fā)出通知
CBConnectPeripheralOptionNotifyOnDisconnectionKey: 在應(yīng)用掛起后,如果與指定的peripheral斷開連接,則發(fā)出通知
CBConnectPeripheralOptionNotifyOnNotificationKey: 在應(yīng)用掛起后,指定的peripheral有任何通知都進(jìn)行提示
Core Bluetooth Background Execution Modes
后臺(tái)執(zhí)行模式
如果你的應(yīng)用在后臺(tái)時(shí)也需要處理藍(lán)牙事件,就必須在Info.plist中聲明應(yīng)用要支持藍(lán)牙后臺(tái)模式,這樣,當(dāng)有藍(lán)牙事件發(fā)生時(shí),系統(tǒng)會(huì)喚醒應(yīng)用來處理。
有兩種藍(lán)牙后臺(tái)模式,一種為central角色,另一種為peripheral角色。如果應(yīng)用需要兩種角色,則可以聲明支持兩種模式。
聲明方式:增加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中會(huì)顯示為更加人性化的文本,不是直接顯示實(shí)際的鍵值對(duì)。如要顯示實(shí)際值,可右鍵,或control點(diǎn)擊,在彈出菜單中選擇Show Raw Keys/Values
The bluetooth-central Background Execution Mode
支持central后臺(tái)運(yùn)行的模式
如果你的應(yīng)用支持central角色的后臺(tái)模式,也就是Info.plist中UIBackgroundModes鍵的值中包含bluetooth-central值。那么應(yīng)用將可以在后臺(tái)處理特定的藍(lán)牙相關(guān)事件。即使在后臺(tái),你仍然可以發(fā)現(xiàn)和連接peripherals,可以檢索和讀寫數(shù)據(jù)。并且當(dāng)有CBCentralManagerDelegate or CBPeripheralDelegate 代理事件發(fā)生時(shí),系統(tǒng)會(huì)喚醒應(yīng)用來處理。
需要注意的是,進(jìn)入后臺(tái)時(shí),掃描的處理有些區(qū)別: 1, CBCentralManagerScanOptionAllowDuplicatesKey 這個(gè)鍵會(huì)被忽略,多次發(fā)現(xiàn)同一peripheral會(huì)被合并成一個(gè)發(fā)現(xiàn)事件。 2,如果所有掃描中的應(yīng)用都在后臺(tái),那么你應(yīng)用的掃描間隙會(huì)延長。結(jié)果是,掃描到peripheral的時(shí)間可能會(huì)延長。
這樣做是為了減少輻射節(jié)省電量。
The bluetooth-peripheral Background Execution Mode
支持peripheral后臺(tái)運(yùn)行的模式
如果要支持peripheral角色的后臺(tái)模式,你需要在Info.plist中的增加UIBackgroundModes鍵并在值中包含bluetooth-peripheral值。這樣系統(tǒng)會(huì)喚醒應(yīng)用來處理讀寫和訂閱事件。
藍(lán)牙框架(Core Bluetooth framework)不僅允許你的應(yīng)用被喚醒來處理讀寫和訂閱請(qǐng)求,還允許你的應(yīng)用在后臺(tái)狀態(tài)下發(fā)送廣播。但你必須注意后臺(tái)時(shí)廣播與前臺(tái)時(shí)廣播是不同的。即便如此,你必須注意后臺(tái)與前臺(tái)時(shí)廣播處理的區(qū)別。特別是當(dāng)你的應(yīng)用需要在后臺(tái)發(fā)送廣播。
1,CBAdvertisementDataLocalNameKey 這個(gè)鍵會(huì)被忽略,并且peripheral的local name不會(huì)被廣播
2,CBAdvertisementDataServiceUUIDsKey 的值中包含的所有service uuids都會(huì)被放到“overflow”區(qū)域;只有ios設(shè)備顯示指明在搜索它時(shí)才會(huì)搜索到這些值。
3,如果所有的處于廣播狀態(tài)的應(yīng)用都在后臺(tái),廣播頻率將降低。
Use Background Execution Modes Wisely
明智使用后臺(tái)運(yùn)行模式
雖然為了完成某些事情,有必要把你的應(yīng)用聲明成支持后臺(tái)運(yùn)行模式,你也應(yīng)該要能有效處理后臺(tái)任務(wù)。因?yàn)閳?zhí)行藍(lán)牙任務(wù)會(huì)使用無線電,從而耗費(fèi)電池電量,所以盡量最小化后臺(tái)任務(wù)。應(yīng)用被藍(lán)牙事件喚醒后應(yīng)能盡快處理好任務(wù),以便被重新掛起。
支持后臺(tái)運(yùn)行的任務(wù)要遵循幾個(gè)原則 1,應(yīng)用應(yīng)該是基于會(huì)話的,并提供接口讓用戶決定何時(shí)開始或停止藍(lán)牙事件。 2,應(yīng)用被喚醒后,大約有10秒鐘的時(shí)間來完成任務(wù),所以應(yīng)該盡快完成任務(wù)并重新掛起。若在后臺(tái)花費(fèi)太多時(shí)間,則將受到系統(tǒng)的遏制甚至被扼殺。 3,應(yīng)用不應(yīng)該使用這種被喚醒的機(jī)會(huì)來執(zhí)行與之無關(guān)的事情。
Performing Long-Term Actions in the Background
后臺(tái)長時(shí)間執(zhí)行
一些應(yīng)用需要長時(shí)間后臺(tái)運(yùn)行。舉個(gè)例子,你可發(fā)一款家庭安全應(yīng)用,ios設(shè)備與藍(lán)牙門鎖通訊。當(dāng)用戶離開家時(shí)自動(dòng)鎖門,當(dāng)用戶回到家時(shí)門自動(dòng)打開,整個(gè)過程應(yīng)用都是后臺(tái)運(yùn)行。當(dāng)用戶離開家時(shí),ios與門鎖斷開連接。這是應(yīng)用只簡單調(diào)用connectPeripheral:options:,因?yàn)檫B接沒有超時(shí),ios設(shè)備將在用戶回到家時(shí)重新連接上。 假設(shè)用戶離開家好幾天,并假設(shè)app被系統(tǒng)終止,應(yīng)用將無法在用戶回到家時(shí)重連門鎖,這時(shí)用戶將無法開門。對(duì)于這類應(yīng)用,很重要的一點(diǎn)要能夠繼續(xù)使用藍(lán)牙執(zhí)行長時(shí)事件,如管理活動(dòng)和懸停連接。
State Preservation and Restoration
狀態(tài)保存和還原
因?yàn)闋顟B(tài)保存和還原是藍(lán)牙內(nèi)在支持的,你的應(yīng)用可選擇支持這一特征來讓系統(tǒng)保存central和peripheral manager的狀態(tài),并繼續(xù)執(zhí)行藍(lán)牙任務(wù),即使你的應(yīng)用不在運(yùn)行。當(dāng)任務(wù)完成,系統(tǒng)重新激活應(yīng)用到后臺(tái),讓你的應(yīng)用有機(jī)會(huì)還原狀態(tài)并處理事件。上面說的家庭安全應(yīng)用,系統(tǒng)可以管理連接請(qǐng)求,并在用戶回到家重新連接上藍(lán)牙時(shí)重新激活應(yīng)用來處理 centralManager:didConnectPeripheral:代理回調(diào)。
藍(lán)牙庫支持狀態(tài)保存和還原,支持central角色,peripheral角色。當(dāng)應(yīng)用實(shí)現(xiàn)central角色并增加支持狀態(tài)保存和還原,系統(tǒng)就會(huì)在終止應(yīng)用釋放內(nèi)存前保存central manager對(duì)象的狀態(tài),如果應(yīng)用有多個(gè)central managers,你可選擇哪些對(duì)象你希望系統(tǒng)為你維護(hù)。對(duì)于CBcentralManger 對(duì)象,系統(tǒng)維護(hù)這些:
1,central manager掃描的services和對(duì)應(yīng)的options
2,已連接的和未連接上的peripherals
3,訂閱的characteristics
實(shí)現(xiàn)peirpheral角色的應(yīng)用類似處理。對(duì)于CBPeripheralManager對(duì)象,系統(tǒng)維護(hù)這些:
1,廣播的數(shù)據(jù)
2,peripheral manager發(fā)布到設(shè)備數(shù)據(jù)庫的services和characteristic
3,那些訂閱了你characteristics的值得centrals
當(dāng)應(yīng)用被系統(tǒng)重新激活到后臺(tái),假如應(yīng)用之前有發(fā)現(xiàn)peripheral,你可以重新創(chuàng)建應(yīng)用的central和peripheral manager,并還原他們的狀態(tài)。后面將繼續(xù)說明如何利用狀態(tài)保存與還原。
Adding Support for State Preservation and Restoration
添加狀態(tài)保存與還原的支持
這一特性是可選的,增加步驟如下:
1,(必須)在創(chuàng)建和初始化時(shí)選擇支持狀態(tài)保存和還原。Opt In to State Preservation and Restoration 這一節(jié)將更詳細(xì)描述
2,(必須)在應(yīng)用被系統(tǒng)喚醒時(shí)復(fù)原central或peripheral manager對(duì)象。Reinstantiate Your Central and Peripheral Managers 這里將繼續(xù)描述
3,(必須)實(shí)現(xiàn)還原代理方法。Implement the Appropriate Restoration Delegate Method. 這里將繼續(xù)說明
4,(可選)更新central和peripheral managers的初始化過程。Update Your Initialization Process。這里將繼續(xù)說明
Opt In to State Preservation and Restoration
狀態(tài)保存和還原的設(shè)置
在創(chuàng)建和初始化時(shí),提供唯一的還原id。還原id是字符串,對(duì)于藍(lán)牙庫和應(yīng)用來說,還原id是用來標(biāo)記central或peripheral manger的。你的代碼只關(guān)心這個(gè)字符串,但這個(gè)字符串告訴藍(lán)牙庫需要保存被標(biāo)記對(duì)象的狀態(tài)。藍(lán)牙庫只保存那些有標(biāo)記還原id的對(duì)象的狀態(tài)。
假如,選擇支持狀態(tài)保存和還原的應(yīng)用只有一個(gè)CBCentralMnager對(duì)象實(shí)例實(shí)現(xiàn)了central角色,那么在初始化時(shí)初始化options中增加CBCentralManagerOptionRestoreIdentifierKey 鍵,并賦值還原id.
/*
NSString * const CBCentralManagerOptionShowPowerAlertKey 對(duì)應(yīng)一個(gè)NSNumber類型的bool值,用于設(shè)置是否在關(guān)閉藍(lán)牙時(shí)彈出用戶提示
NSString * const CBCentralManagerOptionRestoreIdentifierKey 對(duì)應(yīng)一個(gè)NSString對(duì)象,設(shè)置管理中心的后臺(tái)模式恢復(fù)標(biāo)識(shí)符ID
*/
myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{ CBCentralManagerOptionRestoreIdentifierKey: @"myCentralManagerIdentifier" }];
peripheral manager的處理也是類似的,key是CBPeripheralManagerOptionRestoreIdentifierKey
注意:因?yàn)閼?yīng)用可以有多個(gè)CBCentralManager 和 CBPeripheralManger實(shí)例。注意每個(gè)還原id都是唯一的,這樣系統(tǒng)才能區(qū)分開來。
Reinstantiate Your Central and Peripheral Managers
還原central和peripheral manager
當(dāng)應(yīng)用被系統(tǒng)喚醒,你需要做的第一件事是使用還原id復(fù)原central and peripheral manager。如果應(yīng)用中只有一個(gè)central or peripheral manager,并且在應(yīng)用的整個(gè)生命周期中存在,那么就簡單了。 如果應(yīng)用使用多個(gè)central or peripheral manager 或如果應(yīng)用使用的manager不是在app的整個(gè)生命周期中存在,那么應(yīng)用需要知道哪些managers需要復(fù)原。在實(shí)現(xiàn)application:didFinishLaunchingWithOptions:這個(gè)代理方法時(shí),通過使用參數(shù)launchoptions中的鍵(UIApplicationLaunchOptionsBluetoothCentralsKey or UIApplicationLaunchOptionsBluetoothPeripheralsKey) 可以獲得應(yīng)用在終止時(shí)為我們保存的還原id列表。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey]; ...
有了還原id列表后,就可以復(fù)原出central manager 對(duì)象了。
注意:當(dāng)應(yīng)用被激活時(shí),系統(tǒng)只提供那些應(yīng)用終止時(shí)有藍(lán)牙任務(wù)的central and peripheral managers 的還原ids.
Implement the Appropriate Restoration Delegate Method
實(shí)現(xiàn)還原代理方法
在重新創(chuàng)建central and peripheral managers之后,需要通過藍(lán)牙系統(tǒng)還原他們的狀態(tài)。對(duì)于central managers,要實(shí)現(xiàn)centralManager:willRestoreState:代理方法,對(duì)于peripheral managers 實(shí)現(xiàn)peripheralManager:willRestoreState: 方法。
重要:對(duì)于使用狀態(tài)保存和還原特性的應(yīng)用,應(yīng)用被激活到后臺(tái)的第一個(gè)代理調(diào)用是centralManager:willRestoreState: and peripheralManager:willRestoreState:。對(duì)于未使用這一特性的應(yīng)用,第一個(gè)代理調(diào)用是centralManagerDidUpdateState: and peripheralManagerDidUpdateState:。
在這些代理中,最后一個(gè)參數(shù)是dictionary,包含了應(yīng)用被終止時(shí)managers的信息??捎面I值參考 Central Manager State Restoration Options constants in CBCentralManagerDelegate Protocol Reference and the Peripheral_Manager_State_Restoration_Options constants in CBPeripheralManagerDelegate Protocol Reference
要還原CBCentralMnager 對(duì)象的狀態(tài),要使用centralManager:willRestoreState:方法中dictionary的鍵值對(duì)。舉個(gè)例子,假如centralmanger對(duì)象在app被終止時(shí)有acitve或pending連接,系統(tǒng)會(huì)繼續(xù)管理他們。就像下面代碼所示,可以使用CBCentralManagerRestoredStatePeripheralsKey 鍵從dictionary中獲取所有設(shè)備的列表,這些設(shè)備就是central manger已連接或正在連接的設(shè)備。
//dict中會(huì)傳入如下鍵值對(duì)
/*
//恢復(fù)連接的外設(shè)數(shù)組
NSString *const CBCentralManagerRestoredStatePeripheralsKey;
//恢復(fù)連接的服務(wù)UUID數(shù)組
NSString *const CBCentralManagerRestoredStateScanServicesKey;
//恢復(fù)連接的外設(shè)掃描屬性字典數(shù)組
NSString *const CBCentralManagerRestoredStateScanOptionsKey;
*/
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)state {NSArray *peripherals = state[CBCentralManagerRestoredStatePeripheralsKey];...
如何使用這個(gè)列表要看具體情況。比如,如果應(yīng)用要維護(hù)central manger 已發(fā)現(xiàn)peripherals的列表,你可能就需要利用到它。參見Connecting to a Peripheral Device After You’ve Discovered It, 請(qǐng)注意在需要給peripheral設(shè)置相應(yīng)的代理。
對(duì)于CBPeripheralManager 對(duì)象,也需要類似的處理,相應(yīng)的代理方法是peripheralManager:willRestoreState:。
Update Your Initialization Process
更新你的初始化進(jìn)程
在前面的三個(gè)步驟之后,你可能想知道central and peripheral manager的初始化進(jìn)程。雖然這是一個(gè)可選步驟,但如果想讓你的應(yīng)用跑起來更流暢,這可是很重要的。假設(shè)應(yīng)用在檢索peripheral的服務(wù)時(shí)被終止。當(dāng)應(yīng)用還原后,它不知道這個(gè)過程到底進(jìn)行到哪一步了。你也想知道從哪一步繼續(xù)。
舉例,當(dāng)在centralManagerDidUpdateState:方法中初始化你的應(yīng)用時(shí),你可以查到在應(yīng)用被終止時(shí)你是否成功發(fā)現(xiàn)被還原peripheral的某個(gè)service,如下:
NSUInteger serviceUUIDIndex = [peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj,NSUInteger index, BOOL *stop) { return [obj.UUID isEqual:myServiceUUIDString];}];if (serviceUUIDIndex == NSNotFound) { [peripheral discoverServices:@[myServiceUUIDString]]; ...
如上,如果系統(tǒng)在應(yīng)用發(fā)現(xiàn)service之前終止它,那么開始搜索peripheral的數(shù)據(jù),使用discoverServices:搜索。如果應(yīng)用在被終止前已搜索到service,那么你需要檢查時(shí)候搜索到你要的characteristics,(如果有訂閱,也檢查是否已訂閱)。通過檢查初始化過程,可以確保在這時(shí)調(diào)用到最合適的方法。