Tuesday, October 18, 2011

Android でのバックグラウンド処理

Androidでバックグランド処理を行うための手法がいくつかあるのでまとめてみた。

Androidでは用途や求めるユーザエクスペリエンスによってバックグラウンド処理のやり方が変わってくる。
大きく分けて、

  • スレッドを使う (Thread)
  • 非同期タスクを使う (AsyncTask)
  • サービスを使う (Service)
  • レシーバを使う (BroadcastReceiver)

といった感じなのだが、それぞれの特徴を上げてみる。
※ NDKとIPC(Interprocess Communication) については触れない。

1.Thread

一番単純かもしれない。単なるスレッドであるが、UIに直接アクセスすることはできない。UIは単一のスレッドで管理されているのでそのスレッドに処理をdelegateしなくてはならない。

サンプル (リファレンスガイドより)

ViewクラスのpostメソッドにRunnableクラスを渡すことで操作をdelegateしている。

注意すべきなのは、Visible Activity がなくなると(pauseやfinishで)、スレッドが処理を継続していてもシステムにプロセスが殺される可能性があることである。

2. AsyncTask

スレッドを使ったバックグラウンド処理であるが、UIには比較的アクセスしやすい(直接アクセスできないのはスレッドと同じ)

サンプル (リファレンスガイドより)

onPreExecute(),onPostExecute(),onProgressUpdate()が実行されるのはUIスレッドであるので、UIの操作が可能となっている。
doInBackground()内でいつでもpublishProgress()を実行することにより、onProgresUpdate()が呼ばれる。

呼び出し元のActivityをfinishしてもAsyncTaskは実行を続けるが、前述のスレッドと同様にVisible Activityがなくなると実行中でも殺される。

3. Service

真にバックグラウンド処理を継続実行できるのはサービスだけであり、サービスはワーカースレッドではなくプロセスのメインスレッドで実行される。

サービスはUIがなくてもシステムに殺されるようなことはないし、安心してバックグラウンド処理を任せることができるが、BindやServiceConnection,AIDLの定義、またメモリの消費という観点で大げさだしインスタントには使いづらい。
そこで、Android SDKにはIntentServiceというベーシックなバックグラウンド処理用のクラスがあるのでこれを使うとよい。

IntentServiceはActivityからはIntentを作成してstartService()になげるだけでよく、Service側はonHandleIntent(Intent intent)でIntentを受け取る。ひとつのIntentを処理中に別の一つまたは複数のIntentがServiceに与えられると、それらはタスクキューとして待機され、後に一つづつ処理される。
UIの操作は基本的にできないがNotification/Toastを表示することは可能で、Toastを表示するにはHandlerを経由させる必要がある。

サンプル

※ AndroidManifest.xmlにサービスとしてクラスを登録する必要がある

また、IntentServiceは必要な時にのみ開始・動作しているのでリソース消費も最低限で済む。全てのタスクキューについて処理が終わると自動的に終了するのでstopSelf()を呼ぶ必要もなく、タスクキュー(Intent)が溜まっていれば継続して実行される。

4. BroadcastReceiver

レシーバは厳密にはバックグラウンド処理用のAPIではないのだが、Activityから特定のACTIONを発行し、受け取ることでバックグラウンド処理を実現することができる。
しかし、BroadcastReceiverのメイン処理関数 onReceive() は10秒以内にreturnしなくてはならないという制約がある。
10秒を超えるとシステムはRecevierがblock(フリーズ)していると判断し、プロセスをkillしてしまう。この制約を逃れるためには、goAsync()メソッドで別スレッドに処理を依頼し、さっさとreturnするという方法があるが、goAsync()メソッドはAPI Level11(Honeycomb)からなので現状では使いづらい。
という具合に時間のかかるバックグラウンド処理には使いづらいし、基本的になんらかのイベントをトリガーにして起動するタイプであることを忘れてはいけない。簡単なのでサンプルは省略する。

まとめ

ActivityのUIに干渉する必要がある処理は AsyncTask, UIに非依存な処理は IntentService という選択をするのがよい。

参考

http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html
http://developer.android.com/reference/android/app/Service.html
http://developer.android.com/reference/android/app/IntentService.html
http://developer.android.com/reference/android/content/BroadcastReceiver.html

1 comment:

shinriyo said...

はじめまして。
タスクマネージャーからBroadcastRecieverへ送信したアプリをキルしてしまっても、Notificationsを生かしたままにしたいとおもっています。
しかし、この方法以外でもできないでしょうか?