ActivityとServiceの連携

Serviceと連携したいときの手順メモ。
基本的にここに書いてあるのと同じ。

基本

1. Service側: IBinderをimplementsしたオブジェクトを用意し、Service#onBind()の戻り値として返す。
2. 呼び出し側: ServiceConnectionをimplementsしたオブジェクトを用意し、これをContext#bindService()に渡す。
3. 呼び出し側: ServiceConnection#onServiceConnectedにIBinderが渡ってくるので、これを取得する。

IBinderを取得すると、これを介してServiceと通信できるようになる。

詳細

Android Developersによると、Serviceとの連携方法には「IBinderをimplementsしたオブジェクトを用意する方法」によって三種類ある。

  • 自分でBinderを継承する。            → 呼び出し側とService側が同一プロセスのときのみ使用可能。
  • Messenger#getBinder()で生成する。     → 呼び出し側とService側が別プロセスでも使用可能。
  • aidlファイルから生成されたstubクラスを継承する。→呼び出し側とService側が別プロセスでも使用可能。

以下詳細を列挙する。

自分でBinderを継承する

呼び出し側とService側が同一プロセスのときのみ使用可能。
デフォルトの設定では全てのコンポーネント(activity,service,receiver)は同一プロセスで実行されるようになっているので、
なにもしなければこの要件は初めから満たしている。
個人的な経験則からいうと、Androidコンポーネントは一部の例外(OutOfMemoryを起こしやすい処理等)を除いては
プロセスを分割すべきでないと感じたので多くの場合はこの方法で十分だと思う。

Messenger#getBinder()で生成する

呼び出し側とService側が別プロセスでも使用可能。
MessengerはAIDLよりも簡易なプロセス間通信を実現するために用意されているもので、その実体は単にAIDLのラッパーである。
Android Developersによると、多くのアプリではAIDLを直接利用する必要はなく、Messengerを使用するべきであると記されている。

Service側
まずHandlerを継承し、handleMessage()をオーバーライドしてメッセージを受信したときの処理を書く。
次に継承したHandlerを引数にしてMessengerのインスタンスを作成し、getBinder()の戻り値をonBind()で返す。

public class MyService extends Service{
  class IncommingHandler extends Handler{
    @Override
    public void handleMessage(Message msg){
      //メッセージを受信したときの処理
    }
  }

  Messenger mServiceMessenger = new Messenger( new IncommingHandler() );

  @Override
  public IBinder onBind(Intent intent) {
    return mServiceMessenger.getBinder();
  }
}

呼び出し側
onServiceConnected()でIBinderからMessengerを生成する。

public void onServiceConnected(ComponentName className, IBinder service) {
  mServiceMessenger = new Messenger(service);
}

メッセージを送るときはこんな感じ。whatには任意のint値を指定できる。

Message msg = Message.obtain(null, what, 0, 0);
try {
  mServiceMessenger.send(msg);
} catch (RemoteException e) {
  e.printStackTrace();
}

MessageオブジェクトはServiceで用意したHandlerのhandleMessageメソッドに渡される。
上記の方法はServiceで生成したMessengerを呼び出し側に渡す方法だが、このあと呼び出し側で生成したMessengerをServiceに渡すことができる。

Message msg = Message.obtain(null, what, 0, 0);
msg.replyTo = mActivityMessenger;
try {
  mServiceMessenger.send(msg);
} catch (RemoteException e) {
  e.printStackTrace();
}

同じくService側のhandleMessage()に渡される。

aidlファイルから生成されたstubクラスを継承する

呼び出し側とService側が別プロセスでも使用可能。
Androidのプロセス間通信メカニズムであるAIDLを使用する。
詳細は省略。