2012/07/01

Android:Serviceの基本とonStartCommandの戻り値による動作の違い


開始状態
startService()の呼び出しでサービスを開始すると、サービスは"開始状態"となります。
"開始状態"となったサービスは、システムからkillされるか明示的に終了しない限り停止しません。

バインド状態
bindService()を呼び出してサービスにバインドすると"バインド状態"となります。
バインドされたサービスは、バインドしたコンポーネントとの双方向通信やメッセージに
よるやりとりを提供します。
"バインド状態"のサービスは全てのコンポーネントがらアンバインドされることで停止し
ます。

開始+バインド状態
サービスがstartService()とbindService()両方で起動されると"開始状態"かつ
"バインド状態"となります。
この状態のサービスは、両方の状態の停止条件を満たした場合に停止します。
つまり、全てのコンポーネントからアンバインドされてもstopService/stopSelfが呼ばれ
ないと停止しません。
反対に、stopService/stopSelfが呼ばれても全てのコンポーネントからアンバインドされ
ないと停止しません。
この状態をとるサービスの実装は複雑になりがちです。可能であれば分割するほうが良い
でしょう。


基本メソッド

onStartCommand()
startServiceでサービスが開始要求を受けたときのコールバックです。
このメソッドはサービスの開始ポイントとなります。
このメソッドの戻り値は、サービスがシステムから不意にkillされた場合の動作を決定し
ます。
bindServiceを呼び出してサービスをバインドした場合、このメソッドは呼ばれません。

onBind()
bindServiceサービスがバインドされた時のコールバックです。
サービスがバインドを拒否したい場合はnullを返すようにします。

onCreate()
サービスが最初に作成された時のコールバックです。
このメソッドはonStartCommandやonBindよりも前に呼び出され、サービスが起動中の場合
は呼ばれません。

onDestroy()
サービスが破棄される時のコールバックです。



サービスの強制終了

サービスはシステムによる強制終了と再起動を考慮した設計にする必要があります。
サービスがバックグラウンドで長時間実行し続けるほど、システムにより強制終了を受け
る確率が高くなります。
システムによりサービスが強制終了されても、リソースが再利用可能になればサービスを
再起動します。
ただし、onStartCommandの戻り値によってサービスの強制終了時の動作を買えることは
可能です。



onStartCommandの戻り値

onStartCommandメソッドの戻り値でサービス強制終了の振る舞いを制御することができます。

START_NOT_STICKY
サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。

START_STICKY
システムはサービスを新たにインスタンス化し、サービスの再起動を行います。
サービスを起動するペンディングインテントが存在しない場合、システムはintentをnull
にしてonStartCommandを呼び出します。
つまり、onStartCommandの引数intentがnullである状態があり得ます。
また、startServiceによりサービスを複数回起動していたとしても再起動は1度しか行わ
れません。

START_REDELIVER_INTENT
システムはサービスを新たにインスタンス化し、サービスの再起動を行います。
再起動時のonStartCommandには、強制終了前と同じ内容のIntentが渡されます。
再起動順序は強制終了前の起動順序と同じです。(A⇒Bで起動した場合、A⇒Bで再起動)
また、startServiceによりサービスを複数回起動していた場合は、起動した回数分
onStartCommandが呼ばれます。

START_STICKY_COMPATIBILITY
システムにより再起動されることが保障されません。
これはSTART_STICKYとの互換性のために用意されています。
このモードが指定されている場合、onStartCommandの引数intentにnullが格納されること
はありません。


各戻り値を指定した場合、再起動のonStartCommandの引数がどうなるかを検証
(見方)
●<戻り値に指定する定数名>
onStartCommand #<サービスインスタンスID>
- 引数intentの内容
- 引数flagsの内容(0x01:START_FLAG_REDELIVERY / 0x02:START_FLAG_RETRY)
- 引数startIdの内容
--------------------

●START_NOT_STICKY
- 再起動前
onStartCommand #1098310008
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService }
- flags   = 0
- startId = 1

... 強制終了 ...

- 再起動後

再起動されない。
--------------------

●START_STICKY
- 再起動前
onStartCommand #1098310008
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService }
- flags   = 0
- startId = 1
onStartCommand #1098310008
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService }
- flags   = 0
- startId = 2

... 強制終了 ...

- 再起動後
onStartCommand #1098245632
- intent  = null
- flags   = 0
- startId = 2

再起動前はサービスを2つ起動しているが、再起動後のサービス起動は1つだけ。
--------------------

●START_STICKY_COMPATIBILITY
- 再起動前
onStartCommand #1098310008
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService }
- flags   = 0
- startId = 2

... 強制終了 ...

- 再起動後

onStartCommandが呼ばれない(ただしサービスのonCreateはされている)
これは端末or端末状態依存のような気がする。
--------------------

●START_REDELIVER_INTENT
- 再起動前
onStartCommand #1098310008
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService }:1098309208
- flags   = 2
- startId = 1
onStartCommand #1098310008
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService (has extras) }:1098314896
- flags   = 2
- startId = 2

... 強制終了 ...

- 再起動後
onStartCommand #1098245632
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService }:1098225896
- flags   = 3
- startId = 1
onStartCommand #1098245632
- intent  = Intent { cmp=yuki.test/.ServiceTest$MyService (has extras) }:1098226304
- flags   = 3
- startId = 2

再起動前と同じ順番で再起動してくれる。
Intentも渡ってきています。
--------------------



どうやらSTART_STICKYはSTART_REDELIVER_INTENTに比べて再起動が早いようで、強制終了
から約10秒程で再起動してきます。
START_REDELIVER_INTENTは再起動までに約1分程かかるようです。

flagsの値は端末によって異なるようです。
START_FLAG_REDELIVERYのON/OFFは期待通りですが、START_FLAG_RETRYが初回起動時でON
になる端末があったり、そうならない端末があったりします。
この辺の詳細は未調査。

以上です。