サービス

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 メソドの呼び出しが可能

うーん、何だか理解としては微妙だし、ヤヤコしい。
あと bind とか unbind とかは onStart/onStop で、な方が良い模様。これは LocationManager 云々も同様かと思ったらこちらは onPause/onResume でヤッてるな。