ServiceのBind
ServiceはBindすることで、特定のActivityやサービスに依存させることが出来る。
BindしたサービスはUnbindすると、自動的に破棄される。
BindしたサービスはUnbindすると、自動的に破棄される。
ActivityとServiceの間でやりとりするにはBindする必要があるが、
Unbindすると自動で破棄されてしまうため、BackボタンでHomeに戻るとServiceが意図せず終了してしまい困った。
(HomeボタンならOKなのだが、BackボタンではNGというのはいただけない)
Unbindすると自動で破棄されてしまうため、BackボタンでHomeに戻るとServiceが意図せず終了してしまい困った。
(HomeボタンならOKなのだが、BackボタンではNGというのはいただけない)
Serviceのライフサイクル
Serviceには2つの異なるライフサイクルがある。
1つは、Startに始まり、Stopに終わるパターン。Stopは外からIntentによりStopする場合と、Service自身がStopSelfする場合と、2つの方法がある。
もう1つは、Bindに始まり、Unbindに終わるパターン。
もう1つは、Bindに始まり、Unbindに終わるパターン。
説明すると長くなるので、詳しくは本家をどうぞ
http://developer.android.com/guide/components/services.html
http://developer.android.com/guide/components/services.html
ServiceのStartとBindの合せ技
Serviceのライフサイクルが複雑になるので推奨はしない。
ServiceをStartさせることで、BindしUnbindしてもServiceが破棄されないように小細工できる。
ただしStartさせたサービスは、外からStopするか、Service自身にStopSelfさせる必要がある。
そのためUnbindされると、StopSelfのフラグをONにし、Serviceが特定の状態になったらStopSelfが実行されるようにした。
さらにRebindされると、StopSelfのフラグをOFFにする。
ただしStartさせたサービスは、外からStopするか、Service自身にStopSelfさせる必要がある。
そのためUnbindされると、StopSelfのフラグをONにし、Serviceが特定の状態になったらStopSelfが実行されるようにした。
さらにRebindされると、StopSelfのフラグをOFFにする。
実際にやってみたが、動作が不安定でリリースするアプリには載せられない。
その後、いろいろ試してみて、安定して使える動作になった。
ポイントは
その後、いろいろ試してみて、安定して使える動作になった。
ポイントは
- ライフサイクルを出来る限り短く。
- 変化する状態は少なく
- bindはごく短時間で終わらせる。Connectedが呼ばれたら、その中でUnbindしてしまう作りにした。
サンプル
Activityの定義、抜粋。
public class MainActivity extends Activity implements MyService.Listner {
onCreateでstartServiceとbindServiceをコール。startServiceを呼ばずにbindService単独でAUTO_CREATEを付けてServiceを起動させるとと、Unbindで死んでしまう。
public void onCreate(Bundle bundle) { super.onCreate(bundle); Intent intent = new Intent(this, MyService.class); intent.putExtra(MyService.KEY_COMMAND, MySerVice.COMMAND.HELLO); startService(intent); bindService(intent, mMyServiceConnection , 0); }
リスナーを渡したらすぐさまUnbind。
ServiceConnection mMyServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { Log.v(TAG, "Service connected"); TimerService timerService = ((MyService.MyServiceBinder)service).getTimerService(); timerService.setListner(MainActivity.this); unbindService(mTimerServiceConnection); } public void onServiceDisconnected(ComponentName name) { Log.v(TAG, "Service disconnected"); } };
stopServiceではなくstartServiceでServiceを終了させる。Serviceの状態によっては、すぐに終了せず、時間を置いてから自滅させたかったのでこうした。
public void onDestroy() { super.onDestroy(); Intent intent = new Intent(this, TimerService.class); intent.putExtra(TimerService.KEY_COMMAND, TimerService.COMMANDS.BYE); startService(intent); }
Serviceの定義、抜粋。
public interface TimerListner { void onServiceStarted(CharSequence message); }
onStartCommandでExtraを取りだし、Activityでセットされた値に応じて処理を変える
@Override public int onStartCommand(Intent intent, int flags, int id) { Log.v(TAG, "onStartCommand"); COMMANDS command = (COMMANDS)intent.getSerializableExtra(KEY_COMMAND); switch(command) { case BYE: killTimer = new Timer(); ... ... public void run() { stopSelf(); } ... ... break; default: break; } if (mListner != null) mListner.onServiceStarted("hello"); return START_STICKY; } public void onDestory() { if (killerTimer != null) killerTimer.cancel(); }
UnbindではTRUEを返す。そうしないと再Bind時に、RebindもBindも呼ばれないことがある。APIレベルに依るのか?
@Override public IBinder onBind(Intent intent) { Log.v(TAG, "onBind"); return mBinder; } @Override public void onRebind(Intent intent) { Log.v(TAG, "onRebind"); return; } @Override public boolean onUnbind(Intent intent) { Log.v(TAG, "onUnbind"); return true; }
vs