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