對於ios應用,你必須要清楚它是在前台運行,還是在後台運行。因為資源有限,你要對這兩種模式區別處理。
默認情況下,當應用進入後台或掛起時,藍牙任務是不執行的。但是,你可以把應用聲明為支持藍牙後台執行模式,這樣當有藍牙相關事件發生時,你的應用就可以被喚醒來處理任務。即使你的應用不要求後台處理支持,當有重要的事件發生時,系統仍然可能跳出警告,要求處理。
即使你的應用支持一種或兩種都支持後台執行模式,也不是就一定能永遠執行。在某些情況下,系統可能終止你的應用以便為前台應用讓出內存,這將導致當前活動和連接等信息丟失。自ios7之後,藍牙庫支持保存狀態信息,並可在下次啟動app時還原狀態信息。你可以通過這個特性來實現長連接。
只支持前台運行的應用
大部分的apps,除非你要求後台運行,在進入後台後,應用會很快被掛起。在掛起狀態下,應用無法處理藍牙相關任務,無法接收藍牙事件。直到重新回到前台。
在central端,只支持前台運行的應用,在進入後台或被掛起時就無法掃描和發現peripheral的廣播包。如果是在peripheral端,廣播將停止,任何central想訪問characteristic的值都將收到異常信息。
不同情況下,默認的行為可能會影響你的程序。比如,在你與peripheral交互數據時,應用掛起(比如用戶切到另一個應用)。這時連接可能會斷開,你並不會收到通知,直到應用重新激活。
Take Advantage of Peripheral Connection Options
利用peripheral連接選項
只支持前台的藍牙應用在掛起後發生的藍牙事件會被系統排隊,並在應用進入前台時把事件發給應用。當特定的central事件發生時,藍牙庫可以提供一種方式來提示用戶。用戶可以根據這些提示來決定是否激活應用。
若想利用使用這些提示,你需要在調用connectPeripheral:options: 方法時傳入如下參數。
CBConnectPeripheralOptionNotifyOnConnectionKey: 在應用掛起後,與指定的peripheral成功建立連接,則發出通知
CBConnectPeripheralOptionNotifyOnDisconnectionKey: 在應用掛起後,如果與指定的peripheral斷開連接,則發出通知
CBConnectPeripheralOptionNotifyOnNotificationKey: 在應用掛起後,指定的peripheral有任何通知都進行提示
後台執行模式
如果你的應用在後台時也需要處理藍牙事件,就必須在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,如果所有的處於廣播狀態的應用都在後台,廣播頻率將降低。
明智使用後台運行模式
雖然為了完成某些事情,有必要把你的應用聲明成支持後台運行模式,你也應該要能有效處理後台任務。因為執行藍牙任務會使用無線電,從而耗費電池電量,所以盡量最小化後台任務。應用被藍牙事件喚醒後應能盡快處理好任務,以便被重新掛起。
支持後台運行的任務要遵循幾個原則
1,應用應該是基於會話的,並提供接口讓用戶決定何時開始或停止藍牙事件。
2,應用被喚醒後,大約有10秒鐘的時間來完成任務,所以應該盡快完成任務並重新掛起。若在後台花費太多時間,則將受到系統的遏制甚至被扼殺。
3,應用不應該使用這種被喚醒的機會來執行與之無關的事情。
後台長時間執行
一些應用需要長時間後台運行。舉個例子,你可發一款家庭安全應用,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.
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已連接或正在連接的設備。
- (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,(如果有訂閱,也檢查是否已訂閱)。通過檢查初始化過程,可以確保在這時調用到最合適的方法。