iOSでのBluetooth通信入門 :CoreBluetooth プログラミングガイド 解説 : Core Bluetoothのバックグラウンド処理(iOSアプリケーション用)

bluetooth-ios_001

目次 > Core Bluetoothのバックグラウンド処理(iOSアプリケーション用)

Apple「CoreBluetoothプログラミングガイド」+独自解説


Core Bluetoothのバックグラウンド処理(iOSアプリケーション用)

iOSアプリケーションでは、フォアグラウンド/バックグラウンドのどちらで動作しているかを認識することが重要です。iOSデバイスはリソースの制約が厳しいので、バックグラウンド状態になったら、フォアグラウンドのときとは動作を変える必要があるからです。iOSにおけるマルチタスクの概要については、『iOS App Programming Guide 』の“App States and Multitasking”を参照してください。 バックグラウンド状態や一時停止状態のとき、デフォルトでは、Core Bluetoothに組み込まれた主なタスク(セントラル側、ペリフェラル側とも)が動作しなくなります。とはいえ、Core Bluetoothのバックグラウンド実行モードに対応する旨を宣言しておけば、一時停止状態から起き上がり(ウェイクアップ)、イベントを処理できるようになります。もちろん、あらゆるタスクをバックグラウンドでも動くようにする必要はありませんが、重要なイベントが発生したとき、起こしてもらうようシステムに申し込んでおくことは可能なのです。 Core Bluetoothのバックグラウンド実行モード(セントラル側、ペリフェラル側の一方または両方)に対応したアプリケーションであっても、無制限に動作を続けることはできません。システムがいつでも強制的に停止する可能性があります。メモリを解放し、フォアグラウンドアプリケーションに割り当るためで、そうなると、たとえば動作中/保留中の接続も失われてしまいます。iOS 7以降、セントラル/ペリフェラルマネージャオブジェクトの状態を保存しておき、次のアプリケーション起動時に復元できるようになりました。この機能を活用すれば、長時間にわたるアクションも実装できます。

フォアグラウンド動作のみのアプリケーション

多くのiOSアプリケーションと同様、バックグラウンドタスクの実行許可を要求していない限り、バックグラウンド状態になるとすぐに一時停止状態に移行します。この状態ではBluetooth関係のタスクを実行できませんし、フォアグラウンド状態に戻るまでは、Bluetooth関係のイベントを認識することもありません。
セントラルの側について言うと、フォアグラウンド動作のみのアプリケーション(Core Bluetoothの
バックグラウンド実行モードを宣言していないもの)は、バックグラウンド状態や一時停止状態の
間、アドバタイズしているペリフェラルを走査し、検出することができません。また、ペリフェラルの側も、アドバタイズを出さなくなります。また、あるサービスの特性値をセントラルが取得しようとしても、エラーになってしまいます。
このようなデフォルトの動作は、用途にもよりますが、アプリケーションにさまざまな影響を及ぼします。たとえば、接続先のペリフェラルと、データをやり取りしているとしましょう。その最中に、(たとえばユーザが別のアプリケーションに切り替えたため、)一時停止状態に移行したとします。その間に接続が切れたとしても、フォアグラウンド状態に戻るまでアプリケーションは認識できません。

ペリフェラル接続オプションを活用する

フォアグラウンド動作のみのアプリケーションが一時停止状態の間に、Bluetooth関係のイベントが発生した場合、システムがすべてキューに登録しておき、フォアグラウンド状態に戻ってから配送するようになっています。もっとも、いくつかのイベントについては、一時停止状態であってもユーザに警告する手段があります。ユーザはこの警告に応じて、フォアグラウンド状態に戻すかどうか判断できるのです。
この仕組みを有効にするためには、CBCentralManagerクラスのconnectPeripheral:options:メソッドで接続する際に、次のいずれかのペリフェラル接続オプションを指定してください。

● CBConnectPeripheralOptionNotifyOnConnectionKey – 当該機器との接続に成功したとき、アプリケーションが一時停止状態であれば、その旨の警告を表示するようになります。
● CBConnectPeripheralOptionNotifyOnDisconnectionKey – 当該機器との接続が切れたとき、アプリケーションが一時停止状態であれば、その旨の警告を表示するようになります。
● CBConnectPeripheralOptionNotifyOnNotificationKey – 当該機器から特性値が変化した旨の通知が届いたとき、アプリケーションが一時停止状態であれば、その旨の警告を表示するようになります。

ペリフェラル接続オプションについて詳しくは、『CBCentralManagerClassReference 』の、「Peripheral Connection Options」定数に関する項を参照してください。

Core Bluetoothのバックグラウンド実行モード

バックグラウンド状態でもBluetooth関係のタスクを実行できるようにしたい場合は、Core Bluetoothのバックグラウンド実行モードに対応する旨、情報プロパティリストファイル(Info.plist)に宣言しなければなりません。この宣言があれば、Bluetooth関係のイベントが発生するとシステムが当該アプリケーションを起こす(ウェイクアップ)ようになるので、当該イベントを処理できるのです。心拍モニタのように、定期的にBLEデバイスからデータが送られてくる場合、これが重要な働きをします。

アプリケーションが宣言できるCore Bluetoothのバックグラウンド実行モードは、セントラルとしての役割を実装しているか、ペリフェラルとしての役割を実装しているか、に応じて2種類あります。両方の役割を実装したアプリケーションであれば、両方を宣言しても構いません。実際の宣言は、
UIBackgroundModesキーをInfo.plistファイルに追加し、値として、次のいずれかの文字列を収容した配列を与えることにより行います。

● bluetooth-central – アプリケーションはCore Bluetoothフレームワークを使って、BLEペリフェラルと通信します。
● bluetooth-peripheral – アプリケーションはCore Bluetoothフレームワークを使って、データを
他の機器に公開します。

注意: Xcodeのプロパティリストエディタは通常、多くのキーについて、実際のキー名ではなく、人が読める形の文字列を表示します。Info.plistファイルに現れる実際のキー名を表示したい場合は、エディタウインドウ上でControlキーを押しながらキー名をクリックし、「Show Raw Keys/Values」をオンにしてください。

Info.plistファイルの設定(記述)方法について詳しくは、『Property List Editor Help 』を参照して
ください。

バックグラウンド実行モード「bluetooth-central」

セントラルとしての役割を実装したアプリケーションが、Info.plistファイルにUIBackgroundModes
キーを宣言し、bluetooth-centralという値を与えている場合、バックグラウンドでもBluetooth関
係のタスクを実行できます。バックグラウンド状態でも、ペリフェラルを検出、接続し、データを調査し、やり取りすることができるのです。さらに、CBCentralManagerDelegateや
CBPeripheralDelegateのデリゲートメソッドが呼び出されれば、システムは当該アプリケーション
を起こします(ウェイクアップ)。したがって、接続が確立した、切断された、変更された特性値を受け取った、セントラルマネージャの状態が変化したなど、中心機器としての役割にかかわる重要なイベントを処理できることになります。
バックグラウンド状態でもBluetooth関係の多くのタスクを実行できますが、ペリフェラルを走査する動作は、フォアグラウンド状態のときとは若干異なるので注意してください。具体的には次のような違いがあります。

● 走査オプションキーCBCentralManagerScanOptionAllowDuplicatesKeyは無視されます。また、アドバタイズしているペリフェラルを繰り返し検出しても、1回の検出イベントにまとめら れます。
● ペリフェラルを走査しているアプリケーションがすべてバックグラウンド状態であれば、アドバ タイズパケットを走査する間隔が長くなります。したがって、検出に時間がかかるかも知れませ ん。

 以上のような動作の違いは、電波の送出を減らし、電池の寿命を延ばすのが目的です。

バックグラウンド実行モード「bluetooth-peripheral」

バックグラウンド状態でもペリフェラルとしてのタスクを実行するためには、Info.plistファイル
にUIBackgroundModesキーを宣言し、bluetooth-peripheralという値を与える必要があります。こ
の記述がInfo.plistファイルにあれば、読み取り、書き込み、特性値が変化したときの通知ができ
るよう、必要に応じてシステムがアプリケーションを起こします(ウェイクアップ)。
さらに、バックグラウンド状態でもアドバタイズを送出できるようになります。ただし、アドバタイズを送出する動作は、フォアグラウンド状態のときとは若干異なるので注意してください。具体的には次のような違いがあります。

● アドバタイズオプションキーCBAdvertisementDataLocalNameKeyは無視されます。また、ペリフェラルのローカル名はアドバタイズされません。
● アドバタイズキーCBAdvertisementDataServiceUUIDsKeyの値として記述されているサービスUUIDは、特別な「オーバーフロー」領域に入るため、明示的にこのUUIDを指定して走査しない限り、検出できないようになります。
● アドバタイズを送出するアプリケーションがすべてバックグラウンド状態であれば、送出頻度が落ちるかも知れません。

バックグラウンド実行モードを適切に活かす

アプリケーションの用途によっては、バックグラウンド実行モードの宣言が必須になりますが、その場合でも実行には責任が伴います。Bluetooth関係のタスクの多くは、iOSデバイスの無線機能を積極的に使うので、電池の寿命に大きく影響します。バックグラウンドでの処理は控えめにしてください。Bluetooth関係のイベントに応じて「起こされた」アプリケーションは、できるだけ短時間でイベントを処理し、再び停止状態に戻らなければなりません。
Core Bluetoothのバックグラウンド実行モードを宣言する場合、以下のガイドラインに従ってアプリ
ケーションを実装してください。

● セッションベースで実装し、Bluetooth関係のイベントの配送をいつ開始/停止するか、ユーザが制 御するためのインターフェイスを設けてください。
● 「起こされて」から約10秒以内に、タスクを終了する必要があります。できるだけ短時間で処理 し、一時停止状態に戻るようにしなければなりません。バックグラウンドでの実行に時間がかか りすぎているアプリケーションは、システムによって抑制されるか、強制終了させられる可能性 があります。
● システムが当該アプリケーションを起こす原因となった事象と無関係なタスクを、ついでに実行 するのは避けてください。

 バックグラウンド状態のアプリケーションの動作に関するより一般的な情報が、『iOSAppProgrammingGuide 』の“Being a Responsible Background App” in iOS App Programming Guide に載っています。

長時間にわたるアクションをバックグラウンドで実行する

アプリケーションによっては、Core Bluetoothフレームワークを利用して、長時間にわたるアクションをバックグラウンドで実行することがあります。たとえばiOSデバイス用のホームセキュリティアプリケーションを考えてみましょう。これはBluetooth技術を搭載した施錠システムと通信します。アプリケーションと施錠システムが連携して、ユーザが家を出れば自動的に鍵をかけ、帰ってくれば鍵を開けます。いずれもバックグラウンド状態での動作です。ユーザが家から離れると、iOSデバイスは施錠システムと通信できる範囲外になるため、接続が失われることがあります。するとアプリケーションは、CBCentralManagerクラスのconnectPeripheral:options:メソッドを実行します。接続要求はタイムアウトにならないので、家に帰れば再接続されることになります。
ユーザが数日にわたって家を離れたとしましょう。その間にシステムがアプリケーションを停止したとすれば、家に帰り着いたとき施錠システムに再接続することができず、ユーザは閉め出されてしまうかも知れません。このようなアプリケーションは、Core Bluetoothを使って、長時間にわたり、接続を監視するなどのアクションを実行し続けなければならないのです。

状態の保存と復元

Core Bluetoothには状態の保存/復元機能が組み込まれています。アプリケーションはこれを利用して、セントラル/ペリフェラルマネージャの状態を保存し、アプリケーション自身が停止していても、代わりにBluetooth関係のタスクを実行するよう、システムに要求することができます。タスクが完了すると、システムはアプリケーションを起動し直してバックグラウンド状態にします。したがってアプリケーションは、状態を復元し、イベントを適切に処理できることになります。上述のホームセキュリティアプリケーションの場合、システムは接続要求を監視し、ユーザが帰宅して接続要求の処理が完了した時点でアプリケーションを再度立ち上げます。したがってアプリケーションは、コールバックとして、デリゲートオブジェクトのcentralManager:didConnectPeripheral:メソッドを処理できることになります。

Core Bluetoothは、セントラル/ペリフェラルとしての役割を実装したアプリケーションに代わって、状態の保存と復元を行います。アプリケーションにセントラルとしての役割を実装し、状態の保存/復元機能を組み込んだ場合、システムは、アプリケーションを停止してメモリを解放する際、セントラルマネージャオブジェクトの状態を保存します(セントラルマネージャが複数ある場合、どれをシステムに管理させるか選択可能)。具体的には、指定されたCBCentralManagerオブジェクトについて、次の事項を管理するようになっています。

● セントラルマネージャが走査するサービス(および走査開始時に指定されたオプション)
● セントラルマネージャが接続を試み、あるいは既に接続しているペリフェラル
● セントラルマネージャが変更の通知を申し込んでいる特性
ペリフェラルとしての役割を実装したアプリケーションも、同様にこの機能を活用できます。システムはCBPeripheralManagerオブジェクトについて、次の事項を管理します。
● ペリフェラルマネージャがアドバタイズしたデータ
● ペリフェラルマネージャがデバイスのデータベースに登録したサービスと特性
● 特性値の変化を通知するよう申し込んでいるセントラル

(走査していたペリフェラルを検出したなどのため、)システムがアプリケーションを立ち上げ直
し、バックグラウンド状態に移行させると、アプリケーションはセントラル/ペリフェラルマネージャのインスタンスを改めて生成し、その状態を復元できます。以下の節では、状態の保存/復元機能をアプリケーションに組み込む方法を、詳しく説明します。

状態の保存/復元機能を組み込む

Core Bluetoothの状態保存/復元はオプトイン機能であって、これを活かすためにはアプリケーション側の関与が必要です。その手順を以下に示します。
1. (必須)セントラル/ペリフェラルマネージャを生成/初期化する際に、状態の保存/復元機能をオ
プトインします。詳しくは“状態の保存/復元機能をオプトインする”を参照してください。
2. (必須)システムがアプリケーションを立ち上げ直したときに、セントラル/ペリフェラルマネー
ジャを初期化し直すよう実装します。詳しくは“セントラル/ペリフェラルマネージャを再初期化
する” (38 ページ)を参照してください。
3. (必須)適切に復元を行うデリゲートメソッドを実装します。詳しくは“復元を行う適切なデリ
ゲートメソッドを実装する” (39 ページ)を参照してください。
4. (必要に応じて)セントラル/ペリフェラルマネージャの初期化プロセスを実装し直します。詳し
くは“初期化プロセスを実装し直す” (40 ページ)を参照してください。

状態の保存/復元機能をオプトインする

状態の保存/復元機能をオプトインするためには、セントラル/ペリフェラルマネージャを生成、初期
化する際に、一意的な復元識別子を指定します。復元識別子(restoration identifier)は、Core Bluetoothやアプリケーション自身が、セントラル/ペリフェラルマネージャを識別するために使う文字列です。
その中身はアプリケーション側で自由に決めて構いませんが、この文字列が存在すれば、Core Bluetoothは当該オブジェクトの状態を保存する必要があると認識します。逆に言えば、Core Bluetoothが状態を保存するのは、復元識別子を指定したものに限るということです。

たとえば、CBCentralManagerのインスタンスのみを使ってセントラルとしての役割を実装するアプ
リケーションの場合、これを生成、初期化する際に、初期化オプションとして
CBCentralManagerOptionRestoreIdentifierKeyを与え、セントラルマネージャの復元識別子を指
定してください。

 myCentralManager =

[[CBCentralManager alloc] initWithDelegate:self queue:nil

options:@{ CBCentralManagerOptionRestoreIdentifierKey:

@”myCentralManagerIdentifier” }];

この例には示していませんが、ペリフェラルマネージャオブジェクトを使うアプリケーションの場合も同様です。初期化オプションとしてCBPeripheralManagerOptionRestoreIdentifierKeyを与え、
ペリフェラルマネージャの復元識別子を指定します。

注意: CBCentralManagerやCBPeripheralManagerのインスタンスが複数ある場合、それぞれに一意的な復元識別子を与えて、システムが識別できるようにしてください。

セントラル/ペリフェラルマネージャを再初期化する

システムがアプリケーションを立ち上げ直してバックグラウンド状態にしたとき、まず必要なのは、セントラル/ペリフェラルマネージャを初期化し直すことです。このとき、当初生成したときと同じ復元識別子を渡します。セントラル/ペリフェラルマネージャがひとつだけで、アプリケーションが動作する限り常に活きているならば、ほかに必要な処理はありません。
一方、アプリケーションに複数のセントラル/ペリフェラルマネージャがある、あるいは動作中、常
に活きているわけではない場合、どのマネージャを生成し直すか、きちんと管理する必要がありま
す。アプリケーションが停止する際、システムはマネージャオブジェクトの復元識別子を保存しますが、そのリストを取得できます。アプリケーションデリゲートの
application:didFinishLaunchingWithOptions:メソッドを実装し、引数として渡された起動オプ
ションから、キーUIApplicationLaunchOptionsBluetoothCentralsKeyまたは
UIApplicationLaunchOptionsBluetoothPeripheralsKeyを指定してアクセスしてください。

たとえばセントラルマネージャオブジェクトの復元識別子は、次のようなコードで取得できます。

 – (BOOL)application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

NSArray *centralManagerIdentifiers =

launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];

取得したリストの中からどれが該当するか調べ、適切なセントラルマネージャオブジェクトを再生成してください。

注意: このときシステムがデリゲートメソッドに渡すのは、停止中に何らかのタスクを実行したセントラル/ペリフェラルマネージャの復元識別子だけです。起動オプションのキーについて詳しくは、『UIApplicationDelegate Protocol Reference 』を参照してください。

復元を行う適切なデリゲートメソッドを実装する

該当するセントラル/ペリフェラルマネージャを再生成した後、Bluetoothシステムの状態に同期させ
る、という方法で状態を復元します。(アプリケーションが停止中に)システムが代わって実行した後の状態を再現するため、復元処理用の適切なデリゲートメソッドを実装しなければなりません。具体的には、セントラルマネージャであればcentralManager:willRestoreState:、ペリフェラルマ
ネージャであればperipheralManager:willRestoreState:です。


重要: Core Bluetoothの状態保存/復元機能をオプトインしたアプリケーションの場合、アプリケーションが再起動され、バックグラウンド状態に移行して最初に呼び出されるメソッド(centralManager:willRestoreState:またはperipheralManager:willRestoreState:)
が、Bluetooth関係のタスクをすべて行います。状態保存機能をオプトインしていない(あるいは起動時に復元するべきものがない)場合、最初に呼び出されるデリゲートメソッドは、centralManagerDidUpdateState:またはperipheralManagerDidUpdateState:です。


 

どちらのデリゲートメソッドも、最後の引数として、アプリケーション停止時に保持していたマネージャに関する情報を収容した辞書が渡されます。辞書に現れうるキーの一覧が、
『CBCentralManagerDelegate Protocol Reference 』のCentral Manager State Restoration Options
定数、『CBPeripheralManagerDelegate Protocol Reference 』の
Peripheral_Manager_State_Restoration_Options定数の項にそれぞれ載っています。

CBCentralManagerオブジェクトの状態を復元するには、centralManager:willRestoreState:デリ
ゲートメソッドに渡される辞書に、所定のキーを指定します。たとえば、アプリケーションが停止する時点で、セントラルマネージャに接続中または保留中の接続があった場合、システムが変わって監視を続けます。次のように、CBCentralManagerRestoredStatePeripheralsKeyという辞書キーを
指定することにより、セントラルマネージャが接続し、または接続を試みていたペリフェラル
(CBPeripheralオブジェクトで表す)のリストを取得できます。

 – (void)centralManager:(CBCentralManager *)central

willRestoreState:(NSDictionary *)state {

NSArray *peripherals =

state[CBCentralManagerRestoredStatePeripheralsKey];

この例で、復元したペリフェラルのリストを使って実行するべき処理は、用途に応じて決まります。たとえば、セントラルマネージャが検出したペリフェラルのリストを保持していた場合、復元されたペリフェラルをこのリストに追加し、併せて参照できるようにするかも知れません。“検出したペリフェラルに接続する” で説明したように、適切なコールバックが呼び出されるよう、ペリフェラルのデリゲートを設定してください。
CBPeripheralManagerオブジェクトの状態を復元する場合も同様に、peripheralManager:willRestoreState:デリゲートメソッドに渡される辞書に、所定のキーを指定
します。

初期化プロセスを実装し直す

ここまでの手順(必須)を実装した後、セントラル/ペリフェラルマネージャの初期化プロセスを実
装し直す必要がないか検討してください。これは必須ではありませんが、アプリケーションの円滑な動作には重要かも知れません。たとえば、アプリケーションが停止した時点で、接続先ペリフェラルがどのようなデータを提供するか、調査している最中だったとしましょう。このペリフェラルの状態を復元したとき、停止時点までにサービスをどこまで検出していたかは分かりません。しかし、(最初から検出し直すのではなく、)その時点の状態から続行できる方が望ましいでしょう。
たとえば、デリゲートのcentralManagerDidUpdateState:メソッドでアプリケーションの初期化を
行う際、復元したペリフェラルについて、(停止時点までに)あるサービスが検出済みであったかどうか、次のようにして調べることができます。

 NSUInteger serviceUUIDIndex =

[peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj,

NSUInteger index, BOOL *stop) {

return [obj.UUID isEqual:myServiceUUIDString];

}];

if (serviceUUIDIndex == NSNotFound) {

[peripheral discoverServices:@[myServiceUUIDString]];

このように、サービスを検出している最中にアプリケーションが停止した場合、まずはその時点までに取得できたデータを、discoverServices:メソッドで調査することから始めます。サービスが検
出済みであった場合は次に、必要な特性が検出済みかどうか(さらに、特性値の変化を通知するよう申し込み済みかどうか)を確認するとよいでしょう。このように初期化プロセスを実装し直すことにより、状況に応じて適切なメソッドを呼び出せるようになります。


※ このコンテンツはアップル社の提供する「CoreBluetoothプログラミングガイド」というPDFをwebの形式に変換したものをベースとしています。ページ中に含まれる図やテキストは「CoreBluetoothプログラミングガイド」からの引用が多く含まれます。PDFの形式ではなくウェブの形式で閲覧したい方への利便性を高めることや、「CoreBluetoothプログラミングガイド」だけではわかりにくいという箇所の捕捉を行うことが当コンテンツの主旨です。

 

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です