銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

バックグラウンドタスク (PeriodicTask) でアプリ本体とデータをやり取りする。あるいは Chatwork for WP の作り方

Windows Phone Advent Calendar 2013 - Adventar 24日目のエントリです。24日ですが、クリスマスイブな要素は一切ありません。

さて、Chatwork APIが出たことで、Chatwork for WP(仮)を開発中です。

f:id:tanaka733:20131223183341p:plain

もろもろ利用制限の関係で、APIが正式版にならない限りリリースは難しそうですが...

さて、普通にAPIからデータを取得して表示したり、書き込んだりするのは、tanaka-takayoshi/CSharp.Chatwork.Api · GitHub をPCLで作ってあるおかげでそんなに難しくないのですが、ちょっと困っているのが、メッセージの新着通知です。アプリが起動している間は、適当なタイミングでポーリングでもすればOKなのですが(APIコールの上限には注意)、起動していないときに新着があることをどうやって確認するかが Windows Phoneの場合やっかいです。

理想的には、Chatwork 側に新着時に特定のURLへリクエストを投げるようなWebフック機能を実装してもらって(GithubとかNewRelicにはありますね)、受信したサーバーでWindows PhoneへPush通知を送る、という方法がありそうです。もしくは、WebSocket的な接続でメッセージを受信できるようにしてもらって、同じくサーバーで受信してPush通知を送信する、という方法もあります。が、いずれの方法も Chatwork 側の対応が必要なので現時点で実装は不可能です。

というわけで、Windows Phone側でどうにかしないといけないので、バックグラウンド エージェントを利用することにしました。最大の問題点としては、30分間隔でしか実行できないので、新着通知も30分間隔でしか行えません...とはいえ、ないよりはましなので実装の方法を紹介したいと思います。(おそらく、まだ日本語ではあまり記事になっていないと思うのですが...)

バックグラウンドの概要はこちらを参考にしてください。Windows Phone 7.1から利用可能です。

Windows Phone のバックグラウンド エージェント

特に実装についてはこちらを参考にしています。

Windows Phone のバックグラウンド エージェントを実装する方法

今回は小さな処理を極力短い時間間隔で実行したいので、 PeriodicTask を使います。リンクにあるように、バックグラウンドタスクで行う処理は別プロジェクトとして作成します。というところで気になるのが、アプリ本体とバックグラウンドタスクでデータのやり取りはどうするか?ということです。

少なくともユーザーに入力してもらうAPIのトークン(OAuth認証になった場合はOAuthのトークン)はアプリからバックグラウンドタスクに引き渡す必要があります。また、新着をチェックしたいのですが、APIで取得できるのは未読の件数なので、未読が増えたかどうか調べるには前回の未読件数を保存しておかなければいけません。*1

アプリ本体とバックグラウンドタスクとの間でデータをやり取りするには、IsolatedStrage(分離ストレージ) が使えます。じゃあ、お手軽に IsolatedStrageSettings を使おうと思ったのですが...

フォアグラウンド アプリとバックグラウンド エージェントの間の情報の受け渡しは、エージェントとアプリが同時に実行されるかどうかを予測できないため、困難な場合があります。このための推奨のパターンを次に示します。

  1. 定期的なエージェントおよびリソースを大量に使用するエージェントの場合: LINQ 2 SQL、または Mutex で保護されている分離ストレージのファイルを使用します。フォアグラウンド アプリが書き込みを行い、エージェントが読み取りのみを行う 1 方向の通信では、Mutex で分離ストレージ ファイルを使用することをお勧めします。プロセス間の通信に IsolatedStorageSettings を使用しないことをお勧めします。データが破損する可能性があるからです。

Windows Phone のバックグラウンド エージェントの推奨事項

と書いてあるのを発見しました。お手軽にできないどころが、Mutexを使用して同期をとる必要がありました。そこで、アプリ本体とバックグラウンドタスクの両方から参照できるプロジェクトを新規に作成して、以下のようなクラスを用意しておきます。

Windows Phoneのアプリとバックグラウンドタスクでデータを共有するためのクラス

Mutexを使って同期をとっています。では、このクラスを使って、バックグラウンドタスクを書いてみます。IsolatedStorageからデータを取得=>取得したTokenを使ってAPIから情報取得=>未読件数を比較、という流れです。デバッグ用として、更新がない場合も通知しています。また、SchduleTask の実装クラスで処理を分岐していますが、これは将来的にファイルの送信などのために ResourceIntensiveTask を利用するときには、両方の処理をこの同じメソッド内で記述し、引数でわたってくるSchduleTaskの実装クラスで処理を分岐させることになるからです。 また、デバッグのためにデバッグ時は1分起きにタスクが実行されるようにしておきます。

Windows Phone のバックグラウンドタスクのサンプル。

では、最後にアプリ側の実装を行います。まず、APIから情報を取得し、IsolatedStorage にデータを保存しておきます。そのあと、バックグラウンドタスクを登録します。今回は MainPage に画面遷移した時に一連の処理を実行することにします。こちらもデバッグ用に1分後に最初のタスクが実行されるようにしています。

Windows Phoneでバックグラウンドタスクを登録するサンプル

なお、全体的に Microsoft.Bcl.Asyncを入れたasync/await 構文を使えるようにしています(Chatworl.Apiを入れると入ってきます) これでアプリを実行すると、まず設定からバックグラウンドタスクが登録されているのがわかります。

f:id:tanaka733:20131223183449p:plain

そして、1分おきにタスクが実行され、何も更新がなければこういうトースト通知がでます。

f:id:tanaka733:20131223183521p:plain

更新があると、こういう通知になります。

f:id:tanaka733:20131223184428p:plain

ライブタイルも更新されます。テスト的にやりやすいので、今は自分のタスク数を表示しています。

f:id:tanaka733:20131223232545p:plain

という感じで、開発中のChatwork for WP を紹介しつつ、バックグラウンドタスクでのデータのやりとりについて書いてみました。 また、Chatwork さんにはぜひメッセージ取得APIと軽量な新着チェックの実装を期待しております。

*1:他のデバイスで既読にすることができるので、厳密には未読の件数が減っていても新規の未読が増えていることはありえますが、今のAPIの範囲では確認できないので件数が増えた場合のみ通知することにしました。