歡迎訪問個人博客 muhlenXi,該文章出自個人博客,歡迎轉載,轉載請註明來源: muhlenxi.com/2017/05/03/…。html
在這一節,主要是 iOS APP 關於藍牙後臺處理方面的知識和經驗。數組
對於 iOS APP 來講,知道你的 APP 是運行在前臺仍是運行在後臺很重要。一個 APP 在後臺運行狀態下的行爲表現必須不一樣於前臺,由於 iOS 設備的系統資源是有限的。關於 iOS 後臺運行處理的更多論述 請查閱 Background Execution.xcode
默認狀況下,當你的 APP 在 background(後臺運行)或者處於 suspended state(暫停狀態)時,不管是 Central 端仍是 Peripheral 端,許多常見的 CoreBluetooth 任務是不能被執行的。也就是說,你能夠聲明你的 APP 支持 CoreBluetooth 後臺運行模式來容許你的 APP 進入 suspended state 後依然可以喚醒來處理一些藍牙的事件。即便你的 APP 不須要全面的後臺處理支持,可是當有重要事件發生的時候也須要系統給你發出彈框提醒。安全
即便你的 APP 無論支持哪個 CoreBluetooth 後臺運行模式,它也不能一直在後臺運行。在某個時刻,系統會終止你的 APP 來爲處於前臺的其餘 APP 提供內存空間,從而致使一些活動或者鏈接丟失。好比,對於 iOS 7, CoreBluetooth 支持 Central Manager 和 Peripheral Manager 對象狀態信息的保存和在 APP 啓動時恢復該狀態信息,你可使用這個功能來支持藍牙設備的長期操做。app
除非你請求容許執行特定的後臺任務,不然對於大多數 iOS APP 來講,當你的 APP 進入 background state(後臺狀態)不久後就會處於 suspended state(暫停狀態)。當你的 APP 處於 suspended state 時是不能執行藍牙相關的任務,也不能感知到任何藍牙事件直到從新進入 foreground(前臺)。框架
在 Central 端,沒有聲明支持 CoreBluetooth 後臺運行模式的 APP,也就是隻能前臺運行的 APP,在 background 和 suspended 狀態時,是不能搜索正在廣播數據的 Peripheral。在 Peripheral 端,則是不能進行廣播數據。此時若是一個 Central 嘗試獲取 Peripheral Characteristic 的值會收到一個錯誤。編輯器
根據使用狀況,這種默認行爲可能會以多種方式影響你的 APP,舉個例子,試想一下,當你正在與剛鏈接的 Peripheral 進行數據交互時,此時你的 APP 進入 suspended 狀態(當用戶切換到另外一個 APP時)中,若是 Peripheral 此時失去鏈接,你是不能感知到已經發生 disconnection(斷開鏈接)事件了,直到你的 APP 從新進入 foreground 爲止。ide
只在前臺運行的 APP 處於 suspended 狀態時,系統會將發生的全部藍牙事件加入隊列,只有當 APP 從新進入前臺時,纔會將事件傳遞給 APP,也就是說,當 Central 產生事件時,CoreBluetooth 經過彈框提醒的方式通知用戶。用戶能夠經過這個提醒來決定是否將 APP 從新打開來處理這個特定事件。ui
當調用 CBCentralManager
類的 connectPeripheral:options:
方法鏈接 Remote Peripheral 時,你能夠利用下面這些 Peripheral 鏈接選項來設置彈框提醒。spa
CBConnectPeripheralOptionNotifyOnConnectionKey
-- 若是創建了一個成功的鏈接,此時 APP 進入 suspended 狀態時,若是你想要系統爲給定 Peripheral 顯示一個彈框,你能夠在 options 選項中包含這個鍵。CBConnectPeripheralOptionNotifyOnDisconnectionKey
-- 若是發生了 disconnection 事件,此時 APP 進入 suspended 狀態時,若是你想要系統爲給定 Peripheral 顯示一個彈框,你能夠在 options 選項中包含這個鍵。CBConnectPeripheralOptionNotifyOnNotificationKey
-- 若是收到了 Peripheral 的通知,此時 APP 進入 suspended 狀態時,若是你想要系統顯示一個來自 Peripheral 的通知彈框,你能夠在 options 選項中包含這個件。關於 Peripheral 鏈接參數的更多信息能夠查閱 Peripheral Connection Options 常量。
若是你的 APP 在後臺運行狀態下也須要執行藍牙任務,你必須在 Info.plist
(Infomation property list) 文件中聲明你的 APP 支持 CoreBluetooth 後臺運行模式。當你聲明後,系統會喚醒 suspended 狀態中的 APP 來處理藍牙事件。這對於和每隔一段時間就傳遞數據的 BLE 設備進行交互的 APP 來講很重要,好比一個心率監測器。
APP 可能會聲明的 CoreBluetooth 後臺運行模式有兩種。一種是 APP 扮演了 Central 的角色,另外一種則是 APP 扮演了 Peripheral 的角色。若是你的 APP 同時扮演了這兩種角色,你能夠同時聲明這兩種後臺運行模式。聲明 CoreBluetooth 後臺運行模式的方式就是在 Info.plist
文件中加入一個 UIBackgroundModes
的 Key,Value 則是包含如下提到的兩種字符串的數組:
bluetooth-central
-- APP 使用 CoreBluetooth 框架與其餘 BLE 設備進行通訊。bluetooth-peripheral
-- APP 使用 CoreBluetooth 框架來分享數據。提示:Xcode 的屬性列表編輯器默認顯示的 Key 是人類易讀的字符,而不是實際的 Key 的名字,要在 Info.plist 文件中顯示實際 Key 名,按住 Control鍵並單擊編輯器窗口中的任意 Key,並在彈出的上下文窗口中啓用 Show Raw Keys/Values
項。好比 UIBackgroundModes
的易讀 Key 名則是 Required background modes
,如圖所示
關於如何配置 Info.plist
文件內容的更多信息請查閱 Xcode help
當扮演一個 Central 角色的 APP 在 Info.plist 文件中包含了 UIBackgroundModes
bluetooth-central
鍵值對時,CoreBluetooth 框架容許你的 APP 在後臺運行時執行藍牙任務。當你的 APP 在後臺運行時,你仍舊能夠搜索和鏈接 Peripheral,而後與之進行數據交互。此外,當 CBCentralManagerDelegate
或者 CBPeripheralDelegate
的代理方法進行回調時,系統會喚醒你的 APP 容許你來處理這些 Central 端的重要事件。好比,成功創建了鏈接,鏈接中斷,Peripheral 發送了一個更新值的通知,或者 Central Manager 的狀態發生改變。
儘管你的 APP 在後臺運行時能夠執行許多藍牙任務,可是你要注意這些,在後臺運行狀態下,當你搜索 Peripheral 的操做是不一樣於前臺運行狀態的。特別是當你 APP 在後臺運行狀態下搜索設備:
CBCentralManagerScanOptionAllowDuplicatesKey
將會被忽略,多個廣播數據的 Peripheral 發現事件將合併成一個發現事件。這種策略對下降 Radio 的使用頻率和提升 iOS 設備的續航時間有幫助。
Peripheral 想要在後臺執行藍牙任務,你必須在 Info.plist 文件中包含了 UIBackgroundModes
bluetooth-peripheral
鍵值對,這樣系統會喚醒你的 APP 來處理讀、寫和訂閱事件。
除了容許你的 APP 喚醒來處理讀、寫和來自 Central 的訂閱請求外,CoreBluetooth 框架還容許你的 APP 在後臺運行狀態下廣播數據。也就是說,你應該注意到,在後臺狀態下廣播數據與在前臺狀態下的操做是不一樣的。特別是後臺狀態下廣播數據:
CBAdvertisementDataLocalNameKey
Key 將被忽略,Peripheral 的 local name 也不會廣播。對於通常用戶狀況,可能不須要聲明 APP 支持其中一個或兩個 CoreBluetooth 後臺運行模式,你應該對後臺進程負責,由於執行藍牙的任務須要使用用戶設備的 onboard radio(板載廣播),使用廣播會影響用戶設備的續航時間,應儘可能減小在後臺執行藍牙任務。APP 被藍牙時間喚醒後應迅速處理該事件而後儘量快速的進入 suspended 狀態。
聲明支持 CoreBluetooth 後臺運行模式的 APP 必須遵照一些基本準則:
在後臺運行狀態下,你的 APP 應該如何操做的詳情請查閱 Being a Responsible Background App.
一些 APP 可能須要使用 CoreBluetooth 框架來在後臺運行狀態下執行長期操做,好比,你想要爲 iOS 設備開發一個 安全家庭 APP 來與配備 BLE 技術的門鎖進行通訊。APP 與門鎖交互來自動鎖門(當用戶離開家的時候)和開鎖(當用戶回家的時候),整個期間 APP 一直在後臺運行狀態下。當用戶離家的時候,iOS 設備終會超出門鎖的有效範圍,致使與門鎖的鏈接中斷。在這種狀況下,APP 能夠經過調用 CBCentralManager
類的 connectPeripheral:options:
該方法來創建鏈接,由於鏈接請求不會超時,這樣當用戶到家的時候就會與門鎖從新鏈接。
如今試想一種狀況,當用戶離家有一些天了,若是此時 APP 被系統給終止了。這樣當用戶回家的時候,APP 將不能與門鎖從新鏈接,用戶也不能打開門鎖了。相似這樣的 APP,使用 CoreBluetooth 來執行長期操做相當重要,例如監視活動和掛起鏈接。
由於狀態保存和恢復已經內置於 CoreBluetooth,你的 APP 能夠選擇加入這個功能來要求系統保存 APP 的 Central Manager 和 Peripheral Manager 的狀態,並繼續表明他們執行藍牙任務,即便你的 APP 不在運行,當這些任務完成後,系統會從新讓你的 APP 進入後臺運行狀態並給予必定的時間來保存狀態和處理一些藍牙事件。對於上述描述的安全家庭 APP 這種狀況來講,系統將會監聽鏈接請求,當用戶回家的時候,系統將會從新啓動 APP 來處理 centralManager:didConnectPeripheral:
代理回調和完成鏈接請求。
CoreBluetooth 對 扮演 Central 角色,Peripheral 角色,或者同時都扮演的 APP 都提供狀態保存和恢復支持。當扮演 Central 角色的 APP 增長狀態保存和恢復時,當系統釋放內存空間須要終止你的 APP 時,會保存你的 Central Manager 的狀態,若是你的 APP 擁有多個 Central Manager,你能夠選擇你想要系統跟蹤的 Central Manager,尤爲,對於給定的 CBCentralManager
對象,系統會跟蹤:
扮演 Peripheral 角色的 APP 一樣可使用狀態保存與恢復。對於 CBPeripheralManager
對象,系統會跟蹤:
當你的 APP 被系統置入後臺運行狀態時,你能夠從新實例化 APP 的 Central Manager 和 Peripheral Manager 並恢復他們的狀態。下面的章節會詳細的描述如何在你的 APP 中使用狀態保存和恢復。
CoreBluetooth 的狀態保存和恢復是選擇性加入的功能,須要如下步驟來讓 APP 這一功能生效,你能夠參考一下的步驟來給你的 APP 增長狀態保存和恢復功能:
選擇加入狀態與恢復
小節有詳細描述。從新實例化你的 Central Manager 和 Peripheral Manager
小節有詳細描述。實現恰當的 Restoration Delegate 方法
小節有詳細描述。更新你的初始化進程
小節中有詳細描述。選擇加入狀態保存和恢復功能後,當你 allocate 和 initialize 一個 Central Manager 或者 Peripheral Manager 的時候,你須要提供一個惟一的恢復標識符。恢復標識符是一個標識 Central Manager 或者 Peripheral Manager 的字符串,字符串的值對你的代碼很重要,它將會告訴 CoreBluetooth 須要保存使用該標記的對象,CoreBluetooth 只保存這些擁有恢復標識符的對象。
舉個例子,爲你的 APP (只有一個 CBCentralManager 對象的 Central )選擇加入狀態保存和恢復,當你初始化 Central Manager 的時候須要指定 options(初始化選項),選項是一個以 CBCentralManagerOptionRestoreIdentifierKey
爲 key,Value 是 恢復標識符 的字典。示例代碼以下:
myCentralManager =
[[CBCentralManager alloc] initWithDelegate:self queue:nil
options:@{ CBCentralManagerOptionRestoreIdentifierKey:
@"myCentralManagerIdentifier" }];
複製代碼
雖然上述例子沒有證實這一點,當你給使用 Peripheral Manager 對象的 APP 選擇加入狀態保存與恢復時應使用相似的方式:在你初始化 Peripheral Manager 的時候指定 options(初始化選項),選項是一個以 CBCentralManagerOptionRestoreIdentifierKey
爲 key,Value 是 恢復標識符 的字典。
提示:由於一個 APP 可能擁有多個 CBCentralManager 和 CBPeripheralManager 對象的實例,你應該確保他們的恢復標識符是惟一的,這樣系統就能正確地區分它們。
當你的 APP 被系統從後臺啓動時,你須要作的第一件事就是用狀態恢復標識符從新實例化合適的 Central Manager 和 Peripheral Manager,恢復標識符要跟它們第一次被建立的同樣。若是你的 APP 僅僅用到了一個 Central Manager 或者 Peripheral Manager,而且該 Manager 在你的 APP 生命週期中還存活着,則你就不須要作這一步。
若是你的 APP 使用超過一個以上的 Central Manager 或者 Peripheral Manager,或者使用的 Manager 在 APP 的生命週期中已經死亡了。當你的 APP 被系統啓動時,它須要知道要恢復哪個 Manager 。你能夠經過 APP 終止時保存的 Manager 對象的恢復標識符列表,使用合適的 launch option(啓動選項)參數(UIApplicationLaunchOptionsBluetoothCentralsKey
或者 UIApplicationLaunchOptionsBluetoothPeripheralsKey
)在 application:didFinishLaunchingWithOptions:
代理方法回調中來獲取恢復標識符數組。
舉個例子,當系統啓動 APP 時,你能夠恢復全部 Central Manager 的恢復標識符,示例代碼以下:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSArray *centralManagerIdentifiers =
launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
// ...
}
複製代碼
當你得到恢復標識符列表後,遍歷這個數組來從新實例化合適的 Central Manager 對象。
提示:當 APP 啓動時,系統只會提供要執行一些藍牙任務(此時 APP 沒有在運行)的 Central Manager 和 Peripheral Manager 的恢復標識符。關於啓動選項參數 Keys 的詳情能夠參考 UIApplicationDelegate Protocol Reference. .
當你恢復合適的 Central Manager 和 Peripheral Manager 後,而後將他們的狀態與藍牙系統的狀態同步。爲了使你的 APP 達到系統處理的速度,你必須實現恰當的 Restoration Delegate 方法,對 Central Manager ,須要實現 centralManager:willRestoreState:
代理方法,而對於 Peripheral Manager,則須要實現 peripheralManager:willRestoreState:
代理方法。
重要提示:對於選擇添加 CoreBluetooth 的狀態保存與恢復的 APP,當 APP 從後臺啓動來完成一些藍牙任務時,會第一個調用 centralManager:willRestoreState:
和 peripheralManager:willRestoreState:
代理方法,對於沒有加入狀態與恢復的 APP,會第一個調用 centralManagerDidUpdateState:
和 peripheralManagerDidUpdateState:
代理方法。
以上提到的這些代理方法,最後一個參數是一個字典,該字典包含 APP 終止時系統保存的 Manager 的信息。字典中可用的 Keys,能夠查閱 Central Manager State Restoration Options 和 Peripheral_Manager_State_Restoration_Options 常量。
用 centralManager:willRestoreState:
代理方法提供的字典的 Key 來恢復 CBCentralManager 對象的狀態。舉個例子,若是你的 Central Manager 擁有激活的或者中斷的鏈接(APP 終止時),系統會繼續監視 APP 的行爲,以下所示,你能夠經過 CBCentralManagerRestoredStatePeripheralsKey
Key 去獲得 Central Manager 鏈接的或者嘗試鏈接的 Peripheral 列表(用 CBPeripheral 對象表示)。
- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary *)state {
NSArray *peripherals =
state[CBCentralManagerRestoredStatePeripheralsKey];
// ...
}
複製代碼
當你得到 Peripheral 列表後而後幹什麼取決於使用狀況。舉個例子,若是 APP 有一個 Central Manager 搜索的 Peripheral 列表,你可能想把恢復的 Peripheral 加入該列表中。此時確保要設置 Peripheral 的 Delegate 以保證能收到合適的代理回調。
你能夠經過相似的方法利用 peripheralManager:willRestoreState:
代理方法提供的字典的 keys 來恢復 CBPeripheralManager 的狀態。
當你實現前面必須的步驟後,你可能想要查看 Central Manager 和 Peripheral Manager 的初始化進程。儘管這是一個可選的步驟,但對確保 APP 穩定運行很重要。舉個例子,當正與鏈接的 Peripheral 數據傳輸到一半時,APP 終止了。當你的 APP 恢復這個 Peripheral 時,你不知道 APP 終止時數據處理到哪一步,你想要肯定從哪裏繼續開始處理。
舉個例子,當在 centralManagerDidUpdateState:
代理方法中初始化 APP 時,若是你可以成功的從一個恢復的 Peripheral中找出指定的 Service(在 APP 終止以前),就像這樣
NSUInteger serviceUUIDIndex =
[peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj,
NSUInteger index, BOOL *stop) {
return [obj.UUID isEqual:myServiceUUIDString];
}];
if (serviceUUIDIndex == NSNotFound) {
[peripheral discoverServices:@[myServiceUUIDString]];
// ...
}
複製代碼
以上示例中,若是系統在你調用 discoverServices:
方法完成 Servic 搜索以前終止。若是 APP 搜索 Service 成功,你能夠稍後查看是否發現合適的 Characteristic(一直訂閱的) ,用這種方式來更新初始化進程,你須要在正確的時間調用正確的方法。
一、Core Bluetooth Background Processing for iOS Apps
歡迎在本文下面留言一塊兒交流心得...