iPhoneバックグラウンドでのソケット監視のサンプル
iOS4 からアプリケーションのバックグラウンド動作がサポートされているが、それらはだいぶ制限されたものとなっていて、通常のアプリケーションはバックグラウンドでは最大10分程度しか生存することが出来ない。
それを回避するために Info.plist
に特殊なフラグをたてることで例外的に制限を解除することができるようになっている。そのフラグの種類は
- audio
- location
- voip
であり、それぞれバックグラウンド音声再生、バックグラウンド位置情報取得、バックグラウンドでのソケット監視(voipの着信監視用)に対応している。 そのうち voip だけ今まで扱ったことがなかったので挙動確認のためやってみた、というのが今回のお話。
まず書いてみたコード:
ソケットのハンドリングに libev を使っているので、ビルドを通すには $(HOME)/dev/iphone/lib/libev
に libev がインストールされている必要がある。 また、AppDelegate.m の #define HOST inet_addr("127.0.0.1")
という部分を自分のサーバーのIPになおしてあげるというのも必要。
んで、付属の server.pl が適当に作ったサーバーで、STDIN を読み込んで、入力があったらつながっているiPhoneにその内容を送信するというものになっている。 iPhoneアプリ側はバックグラウンド時にそのパケットを受け取ると受け取った内容をLocalNotificationで表示っていう感じ。
NSInputStream にたいして
[stream setProperty:NSStreamNetworkServiceTypeVoIP
forKey:NSStreamNetworkServiceType];
とかしてあげとくだけで、アプリがサスペンドしたときには OS 側がソケットの監視を代理でやってくれる。 で、そのソケットにたいして何かイベントが起こるとアプリを起こして処理を戻す。アプリ側は通常のソケット操作だけ実装しておけばいい。
このアプリのようにネットワーク周りはCで処理している場合、ファイルディスクリプタを NSStream にアップグレードする必要があってそこは若干めんどう。 Cocoa::EventLoop でもおなじことをしているのでその XS 部分からコピペしてきた。
Skype のバッテリー消費が半端ないのでこの機能にたいしてあまり良い印象はなかったのだが、実際にやってみるとバッテリー消費は思ったより気にならない(いまのところ。あまり検証はしていない)。 おそらく Skype は頻繁にパケットを送受信しているから、アプリが頻繁に起こされ、結果ものすごい勢いでバッテリーが減る、ということなのだろうか。
とにかく、なかなかおもしろい機能。VoIP アプリ以外でもつかえればなー。