サービス
KitchenTimer なソレ (http://bit.ly/6mAZ6g) について簡単に纏めてみます。
クラスが二つあります (内部クラスは除く)。
まずは Main の方から。BroadcastReceiver を継承した KitchenTimerReceiver という内部クラスが定義されています。
private class KitchenTimerReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast toast = Toast.makeText(getApplicationContext(), "Time over!", Toast.LENGTH_LONG); toast.show(); MediaPlayer mp = MediaPlayer.create(Main.this, R.raw.alarm); try { mp.start(); } catch (Exception e) { } } }
上記クラスは Service 側からインテントが発行された場合のレシーバとなります。onCreate で以下の手順で Service を開始しますが
Intent intent = new Intent(this, KitchenTimerService.class); startService(intent); IntentFilter filter = new IntentFilter(KitchenTimerService.ACTION); registerReceiver(receiver, filter);
手順として
- 開始するサービスな intent を生成
- 上記 intent を渡して startService を呼び出す
- IntentFilter の生成 (識別子としての KitchenTimerService.ACTION を渡している)
- registerReceiver 手続きの呼び出し
最後の registerReceiver 手続き呼び出しで KitchenTimerService.ACTION な intent が発行された時には KitchenTimerReceiver なオブジェクトの onReceive 呼び出しね、という事をフレームワークに教えている模様。
receiver という属性は KitchenTimerReceiver クラスの定義部分のすぐ下で定義されています。
private KitchenTimerService kitchenTimerService; private final KitchenTimerReceiver receiver = new KitchenTimerReceiver();
という事でポイントその一は、サービスを起動するクラス側で BroadcastReceiver を継承したクラスを定義しておいてあげれば、サービスからの通知が受信可能。
次にサービスにバインドする、という処理として bindService というメソドを呼び出しています。
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
intent は先に作った開始するサービスな intent なんですが、serviceConnection とは一体何だったか、という事で定義らへんを見ると以下になっている模様。
private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { kitchenTimerService = ((KitchenTimerService.KitchenTimerBinder)service).getService(); } @Override public void onServiceDisconnected(ComponentName className) { kitchenTimerService = null; } };
Connected、Disconnected とある通り、接続時と切断時に呼び出されている模様。上記では属性である kitchenTimerService という変数に何かを代入してます。
Connected な処理ですが IBinder 型の service という引数をキャストして getService というメソドを呼び出していて、それが KitchenTimerService なオブジェクトの参照になっている模様。
これ、KitchenTimerService の中身を見てみると
class KitchenTimerBinder extends Binder { KitchenTimerService getService() { return KitchenTimerService.this; } }
あと Service 側ではバインドされたら以下な callback が呼び出される模様。
@Override public IBinder onBind(Intent intent) { Toast toast = Toast.makeText(getApplicationContext(), "onBind()", Toast.LENGTH_SHORT); toast.show(); return new KitchenTimerBinder(); } @Override public void onRebind(Intent intent) { Toast toast = Toast.makeText(getApplicationContext(), "onRebind()", Toast.LENGTH_SHORT); toast.show(); } @Override public boolean onUnbind(Intent intent) { Toast toast = Toast.makeText(getApplicationContext(), "onUnbind()", Toast.LENGTH_SHORT); toast.show(); return true; }
onBind の戻り型は IBind になっていますが、これはおそらく KitchenTimerBinder が継承している Binder というクラスが実装しているインターフェースと思われます。
で、onBind が戻す IBind なオブジェクトが onServiceConnected の service って引数にセットされて呼び出されるという事かと。
纏め
- Service 起動する側
- Service の生成
- 起動する Service な Intent を生成
- startService メソドにその Intent を渡す
- サービスが識別できる IntentFilter 作る
- registerReceiver メソドに IntentFilter と BroadcastReceiver のサブクラスのインスタンスを渡す
- BroadcastReceiver のサブクラス
- onReceive で bind されたサービス側からの通知を受ける事が可能
- ServiceConnection クラス
- 接続時 (bind 時) に onServiceConnected が呼び出される
- 切断時 (unbind 時) に onServiceDisconnected が呼び出される
- Service へバインド
- bindService メソドに Intent オブジェクトと ServiceConnection オブジェクトと Context.BIND_AUTO_CREATE (通常これ) を渡せば良い
- この時、ServiceConnection#onServiceConnected が呼び出される模様
- 引数で渡される IBinder 型の第二引数は Service 側の onBind メソドの戻り値
- IBinder なオブジェクトを使って Service なオブジェクトの getter を作成しておけばサービス起動側から Service な public メソドの呼び出しが可能
- Service の生成
うーん、何だか理解としては微妙だし、ヤヤコしい。
あと bind とか unbind とかは onStart/onStop で、な方が良い模様。これは LocationManager 云々も同様かと思ったらこちらは onPause/onResume でヤッてるな。