【轉】Core Bluetooth框架之二:後臺處理

原文網址:http://southpeak.github.io/blog/2014/07/31/core-bluetoothkuang-jia-zhi-er-:hou-tai-chu-li/git

在開發BLE相關應用時,因爲應用在後臺時會有諸多資源限制,須要考慮應用的後臺處理問題。默認狀況下,當程序位於後臺或掛起時,大多數普通的Core Bluetooth任務都沒法使用,不論是Central端仍是Peripheral端。但咱們能夠聲明咱們的應用支持Core Bluetooth後臺執行模式,以容許程序從掛起狀態中被喚醒以處理藍牙相關的事件。github

然而,即便咱們的應用支持兩端的Core Bluetooth後臺執行模式,它也不能一直運行。在某些狀況下,系統可能會關閉咱們的應用來釋放內存,覺得當前前臺的應用提供更多的內存空間。在iOS7及後續版本中,Core Bluetooth支持保存Central及Peripheral管理器對象的狀態信息,並在程序啓動時恢復這些信息。咱們可使用這個特性來支持與藍牙設備相關的長時間任務。數據庫

下面咱們將詳細討論下這些問題。網絡

只支持前臺操做(Foreground-Only)的應用

大多數應用在進入到後臺後都會在短期內進入掛起狀態,除非咱們請求執行一些特定的後臺任務。當處理掛起狀態時,咱們的應用沒法繼續執行藍牙相關的任務。併發

在Central端,Foreground-Only應用在進入後臺或掛起時,沒法繼續掃描並發現下在廣告的Peripheral設備。而在Peripheral端,沒法廣告自身,同時Central端對其的任何訪問操做都會返回一個錯誤。app

Foreground-Only應用掛起時,全部藍牙相關的事件都會被系統放入一個隊列,當應用進入前臺後,系統會將這些事件發送給咱們的應用。也就是說,當某些事件發生時,Core Bluetooth提供了一種方法來提示用戶。用戶可使用這些提示來決定是否將應用放到前臺。在《Core Bluetooth框架之一:Central與Peripheral》中咱們介紹了connectPeripheral:options:方法,在調用這個方法時,咱們能夠設備options參數來設置這些提示:框架

  1. CBConnectPeripheralOptionNotifyOnConnectionKey:當應用掛起時,若是有一個鏈接成功時,若是咱們想要系統爲指定的peripheral顯示一個提示時,就使用這個key值。
  2. CBConnectPeripheralOptionNotifyOnDisconnectionKey:當應用掛起時,若是鏈接斷開時,若是咱們想要系統爲指定的peripheral顯示一個斷開鏈接的提示時,就使用這個key值。
  3. CBConnectPeripheralOptionNotifyOnNotificationKey:當應用掛起時,使用該key值表示只要接收到給定peripheral端的通知就顯示一個提示。

Core Bluetooth後臺執行模式

咱們能夠在Info.plist文件中設置Core Bluetooth後臺執行模式,以讓應用支持在後臺執行一些藍牙相關的任務。當應用聲明瞭這一功能時,系統會將應用喚醒以容許它處理藍牙相關的任務。這個特性對於與那種定時發送數據的BLE交互的應用很是有用。ide

有兩種Core Bluetooth後臺執行模式,一種用於實現Central端操做,一種用於實現Peripheral端操做。若是咱們的應用同時實現了這兩端的功能,則須要聲明同時支持兩種模式。咱們須要在Info.plist文件添加UIBackgroundModes鍵,同時添加如下兩個值或其中之一:代理

  1. bluetooth-central(App communicates using CoreBluetooth)
  2. bluetooth-peripheral(App shares data using CoreBluetooth)

bluetooth-central模式

若是設置了bluetooth-central值,則咱們的應用在後臺時,仍然能夠查找並鏈接到Peripheral設備,以及查找相應數據。另外,系統會在CBCentralManagerDelegate或CBPeripheralDelegate代理方法被調用時喚醒咱們的應用,容許應用處理事件,如創建鏈接或斷開鏈接等等。rest

雖然應用在後臺時,咱們能夠執行不少藍牙相關任務,但須要記住應用在先後臺掃描Peripheral設備時仍是不同的。當咱們的應用在後臺掃描Peripheral設備時,

  1. CBCentralManagerScanOptionAllowDuplicatesKey掃描選項會被忽略,同一個Peripheral端的多個發現事件會被聚合成一個發現事件。
  2. 若是掃描Peripheral設備的多個應用都在後臺,則Central設備掃描廣告數據的時間間隔會增長。結果是發現一個廣告的Peripheral設備可能須要很長時間。

這些處理在iOS設備中最小化無線電的使用及改善電池的使用壽命很是有用。

bluetooth-peripheral模式

若是設置了bluetooth-peripheral值,則咱們的應用在後臺時,應用會被喚醒以處理來自於鏈接的Central端的讀、寫及訂閱請求,Core Bluetooth還容許咱們在後臺進行廣告。與Central端相似,也須要注意先後臺的操做區別。特別是在廣告時,有如下幾點區別:

  1. CBAdvertisementDataLocalNameKey廣告key值會被忽略,Peripheral端的本地名不會被廣告
  2. CBAdvertisementDataServiceUUIDsKey鍵的全部服務的UUID都被放在一個」overflow」區域中,它們只能被那些顯示要掃描它們的網絡設備發現。
  3. 若是多個應用在後臺廣告,則Peripheral設備發送廣告包的時間間隔會變長。

在後臺執行長(Long-Term)任務

雖然建議儘快完成後臺任務,但有些應該仍然須要使用Core Bluetooth來執行一個長任務。這時就涉及到狀態的保存與恢復操做。

狀態保存與恢復

由於狀態保存與恢復是內置於Core Bluetooth的,咱們的程序能夠選擇這個特性,讓系統保存Central和Peripheral管理器的狀態並繼續執行一些藍牙相關的任務,即便此時程序再也不運行。當這些任務中的一個完成時,系統會在後臺重啓程序,程序能夠恢復先前的狀態以處理事件。Core Bluetooth支持Central端、Peripheral端的狀態保存與恢復,也能夠同時支持二者。

在Central端,系統會在關閉程序釋放內存時保存Central管理器對象的狀態(若是有多個Central管理器,咱們能夠選擇系統跟蹤哪一個管理器)。對於給定的CBCentralManager對象,系統會跟蹤以下信息:

  1. Central管理器掃描的服務
  2. Central管理器嘗試或已經鏈接的Peripheral
  3. Central管理器訂閱的特性

在Peripheral端,對於給定的CBPeripheralManager對象,系統會跟蹤如下信息:

  1. Peripheral管理器廣告的數據
  2. Peripheral管理器發佈到設備數據庫的服務和特性
  3. 訂閱Peripheral管理器的特性值的Central端

當系統將程序重啓到後臺後,咱們能夠從新從新初始化咱們程序的Central和Peripheral管理器並恢復狀態。咱們接下來將詳細介紹一下如何使用狀態保存與恢復。

添加狀態保存和恢復支持

Core Bluetooth中的狀態保存與恢復是可選的特性,須要程序的支持才能工做。咱們能夠按照如下步驟來添加這一特性的支持:

  1. (必要步驟)當分配及初始化Central或Peripheral管理器對象時,選擇性加入狀態保存與恢復。
  2. (必要步驟)在系統重啓程序時,從新初始化Central或Peripheral管理器對象
  3. (必要步驟)實現適當的恢復代理方法
  4. (可選步驟)更新Central或Peripheral管理器初始化過程
選擇性加入狀態保存與恢復

爲了選擇性加入狀態保存與恢復特性,在分配及初始化Central或Peripheral管理器時提供一個一個惟一恢復標識。恢復標識是一個字條串,用來讓Core Bluetooth和程序標識Central或Peripheral管理器。字符串的值只在本身的代碼中有意義,但這個字符串告訴Core Bluetooth咱們須要保存對象的狀態。Core Bluetooth只保存那些有恢復標識的對象。

例如,在實現Central端時,爲了選擇性加入狀態保存與恢復特性,在初始化CBCentralManager對象時,能夠指定初始化選項CBCentralManagerOptionRestoreIdentifierKey,並提供一個恢復標識,以下代碼所示:

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

實現Peripheral端時的操做也相似,只不過咱們使用選項CBPeripheralManagerOptionRestoreIdentifierKey鍵。

由於程序能夠有多個Central或Peripheral管理器,因此須要確保恢復標識是惟一的,這樣系統能夠區分這些管理器對象。

從新初始化Central或Peripheral管理器對象

當系統重啓程序到後臺後,咱們所須要作的第一件事就是使用恢復標識來從新初始化這些對象。若是咱們的應用只有一個Central管理器或Peripheral管理器,且管理器在程序的整個生命週期都存在,則後續咱們便不須要再作更多的處理。但若是咱們有多個管理器,或者管理器不是存在於程序的整個生命週期,則系統重啓應用時,咱們須要知道從新初始化哪個管理器。咱們能夠經過在程序代理對象的application:didFinishLaunchingWithOptions:方法中,使用合適的啓動選項鍵來訪問管理器對象的列表(這個列表是程序關閉是系統爲程序保存的)。

下面代碼展現了程序從新啓動時,咱們獲取全部Central管理器對象的恢復標識:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];

    // TODO: ...

    return YES;
}

有了這個恢復標識的列表後,咱們就能夠從新初始化咱們所須要的管理器對象了。

實現適當的恢復代理方法

從新初始化Central或Peripheral管理器對象後,咱們經過使用藍牙系統的狀態同步這些對象的狀態來恢復它們。此時,咱們須要實現一些恢復代理方法。對於Central管理器,咱們實現centralManager:willRestoreState:代理方法;對於Peripheral管理器管理器,咱們實現peripheralManager:willRestoreState:代理方法。

對於選擇性加入保存與恢復特性的應用來講,這些方法是程序啓動到後臺以完成一些藍牙相關任務所調用的第一個方法。而對於非選擇性加入特性的應用來講,會首先調用centralManagerDidUpdateState:和peripheralManagerDidUpdateState:代理方法。

在上述兩個代理方法中,最後一個參數是一個字典,包含程序關閉時保存的關於管理器的信息。以下代碼所示,咱們可使用CBCentralManagerRestoredStatePeripheralsKey鍵來獲取Central管理器已鏈接的或嘗試鏈接的全部Peripheral設備的列表:

- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)state
{
    NSArray *peripherals = state[CBCentralManagerRestoredStatePeripheralsKey];

    // TODO: ...
}

獲取到這個列表後,咱們即可以根據須要來作處理。

更新初始化過程

在實現了前面的三個步驟後,咱們可能想更新咱們的管理器的初始化過程。雖然這一步是可選的,但若是要確認任務是否運行正常時,很是有用。例如,咱們的程序可能在解析所鏈接的Peripheral設備的數據的過程當中被關閉。當程序使用這個Peripheral設備做恢復操做時,沒法知道數據處理到哪了。咱們須要確保程序從數據操做中止的位置繼續開始操做。

又以下面的代碼展現了在centralManagerDidUpdateState:代理方法中初始化程序操做時,咱們能夠找出是否成功發現了被恢復的Peripheral設備的指定服務:

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

如上例所述,若是系統在程序完成搜索服務時關閉了應用,則經過調用discoverServices:方法在關閉的那個點開始解析恢復的Peripheral數據。若是程序成功搜索到服務,咱們能夠確認是否搜索到正確的特性。經過更新初始化過程,咱們能夠確保在正確的時間調用正確的方法。

小結

雖然咱們可能須要聲明應用支持Core Bluetooth後臺執行模式,以完成特定的任務,但老是應該慎重考慮執行後臺操做。由於執行太多的藍牙相關任務須要使用iOS設備的內置無線電,而無線電的使用會影響到電池的壽命,因此儘可能減小在後臺執行的任務。任何會被藍牙相關任務喚醒的應用應該儘快處理任務並在完成時從新掛起。

下面是一些基礎的準則:

  1. 應用應該是基於會話的,並提供接口以容許用戶決定何時開始及結束藍牙相關事件的分發。
  2. 一旦被喚醒,一個應用大概有10s的時間來完成任務。理想狀況下,應用應該儘快完成任務並從新掛起。系統能夠對執行時間太長的後臺任務進行限流甚至殺死。
  3. 應用被喚醒時,不該該執行一些可有可無的操做。
相關文章
相關標籤/搜索