Service - Messengerを使ったProcess間通信
前回、密かに予告していましたが、今日はMessengerを使用したServiceとのやり取りを実装したいと思います。今回も単純にServiceからのCallbackを受けて画面に表示されているTextを更新するということを試してみたいと思います。Android developer siteで紹介しているものの簡易版になります
Messengerとは
MessengerはHandlerに紐づけられます。HandlerにMessengerを紐づけることにより、あるProcess内で動いてるHandlerと別のProcess内で動いているHandlerの間でMessage objectを使用したProcess間通信を可能にします。AIDLでinterfaceを定義する代わりにMessengerを使ったServiceとのProcess間通信をする事ができます
実装の要点を下記にまとめます
Activity
- Handlerを作成
- Handler#handleMessage()をoverrideしServiceからのMessageを受け取った際の処理を実装
- 作成したHandlerからMessengerを作成
- Context#bindService()でServiceとのbindを実装
- Context#unbindService()でServiceとのunbind処理を実装
- ServiceConnectionを拡張したclassを実装
- ServiceConnection#onServiceConnected()で渡される引数、IBinderからServiceのHandlerに紐づけられるMessengerを取得
- ServiceからのCallbackを受け取る為にMessage objectを作成しMessage.replyTo fieldにNo.2で作成したMessangerを格納
- 作成したMessage objectをServiceのHandlerに送信する為にServiceのMessenger、Messenger#send() methodを記述
- 必要であればServiceConnection#onServiceDisconnected()にCallback時の処理を実装
- ServiceからのCallbackを受け取らないようにする為の処理を実装
- Message objectを作成しMessage.replyTo fieldにNo.2で作成したMessangerを格納
- 作成したMessage objectをServiceのHandlerに送信する為にServiceのMessenger、Messenger#send() methodを記述
Service
- manifest xmlにservice tagを追加
- ActivityのMessengerを保持するためのListを作成
- Handlerを作成
- Handler#handleMessage()をoverrideしActivityからのMessageを受け取った際の処理を実装
- Message.replyToに格納されたActivityのMessengerをNo.1で作成したListに格納/削除する処理を実装
- Listに格納したMessengerのMessenger#send() methodを使用しActivityへのCallbackを実装
- Handler#handleMessage()をoverrideしActivityからのMessageを受け取った際の処理を実装
- 作成したHandlerからMessengerを作成
- Service#onBind()の戻り値としてMessenger#getBinder()で取得したIBinderを返す
それでは実装です
Activity
package com.android.practice4; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.widget.TextView; import android.widget.Toast; public class MyActivity extends Activity { private static final String TAG = "MyActivity"; private Context mContext; //Activity用のHandler private Handler mActivityHndlr; //Activity用のMessenger private Messenger mActivityMsgr; //Service用のMessenger private Messenger mServiceMsgr; private ServiceConnection mConnection; private boolean mIsBound; private TextView mTextView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mTextView = (TextView) findViewById(R.id.text_view); mContext = this; //Handlerを作成 mActivityHndlr = new ActivityHandler(); //作成したHandlerからMessengerを作成 mActivityMsgr = new Messenger(mActivityHndlr); //ServiceConnectionを拡張したclassを実装 mConnection = new ServiceConnection(){ //ServiceConnection#onServiceConnected()で渡される引数、IBinderから //ServiceのHandlerに紐づけられるMessengerを取得 public void onServiceConnected(ComponentName arg0, IBinder arg1) { mServiceMsgr = new Messenger(arg1); mTextView.setText("Attached"); try { //ServiceからのCallbackを受け取る為にMessage objectを作成し //Message.replyTo fieldにNo.2で作成したMessangerを格納 Message msg = Message.obtain(null, MyService.SET_LISTENER); msg.replyTo = mActivityMsgr; //Message objectをServiceのHandlerに送信する為に //ServiceのMessenger、Messenger#send() methodを記述 mServiceMsgr.send(msg); } catch (RemoteException e) { e.printStackTrace(); } Toast.makeText(mContext, "onServiceConnected", Toast.LENGTH_SHORT).show(); } //必要であればServiceConnection#onServiceDisconnected()にCallback時の処理を実装 public void onServiceDisconnected(ComponentName name) { mServiceMsgr = null; mTextView.setText("Disconnected"); Toast.makeText(mContext, "onServiceDisconnected", Toast.LENGTH_SHORT).show(); } }; } @Override protected void onResume() { super.onResume(); Intent intent = new Intent(this, MyService.class); //Context#bindService()でServiceとのbindを実装 bindService(intent, mConnection, BIND_AUTO_CREATE); mIsBound = true; mTextView.setText("Bound"); } @Override protected void onPause() { super.onPause(); if(mIsBound){ if(mServiceMsgr != null){ //ServiceからのCallbackを受け取らないようにする為の処理を実装 //Message objectを作成しMessage.replyTo fieldにNo.2で作成したMessangerを格納 Message msg = Message.obtain(null, MyService.UNSET_LISTENER); //作成したMessage objectをServiceのHandlerに送信する為に //ServiceのMessenger、Messenger#send() methodを記述 msg.replyTo = mActivityMsgr; try { mServiceMsgr.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } //Context#unbindService()でServiceとのunbind処理を実装 unbindService(mConnection); mIsBound = false; mTextView.setText("Unbound"); } } @Override protected void onDestroy() { super.onDestroy(); } class ActivityHandler extends Handler { //Handler#handleMessage()をoverrideしServiceからのMessageを受け取った際の処理を実装 @Override public void handleMessage(Message msg) { switch (msg.what) { case MyService.TEXT_UPDATE: mTextView.setText("Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } } }
manifest xml抜粋
<!-- manifest xmlにservice tagを追加 --> <service android:name=".MyService" android:process=":remote"/>
Service
package com.android.practice4; import java.util.ArrayList; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; public class MyService extends Service { private static final String TAG = "MyService"; public static final int SET_LISTENER = 0; public static final int UNSET_LISTENER = 1; public static final int TEXT_UPDATE = 2; //Service用のHandler private Handler mServiceHndlr; //Service用のMessenger private Messenger mServiceMsgr; private Context mContext; //ActivityのMessengerを保持するためのListを作成 private ArrayList<Messenger> mListenerMsgrs; private int mValue; private int mCount; @Override public void onCreate() { super.onCreate(); mContext = this; //ActivityのMessengerを保持するためのListを作成 mListenerMsgrs = new ArrayList<Messenger>(); //Handlerを作成 mServiceHndlr = new ServiceHandler(); //作成したHandlerからMessengerを作成 mServiceMsgr = new Messenger(mServiceHndlr); mCount = 1; } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent arg0) { Toast.makeText(mContext, "onBind", Toast.LENGTH_SHORT).show(); mServiceHndlr.sendEmptyMessage(TEXT_UPDATE); //Service#onBind()の戻り値としてMessenger#getBinder()で取得したIBinderを返す return mServiceMsgr.getBinder(); } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); } //Handler#handleMessage()をoverrideしActivityからのMessageを受け取った際の処理を実装 class ServiceHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what) { case SET_LISTENER: //Message.replyToに格納されたActivityのMessengerを //No.1で作成したListに格納する処理を実装 mListenerMsgrs.add(msg.replyTo); break; case UNSET_LISTENER: //Message.replyToに格納されたActivityのMessengerを //No.1で作成したListから削除する処理を実装 mListenerMsgrs.remove(msg.replyTo); break; case TEXT_UPDATE: mValue = msg.arg1; for (int i = mListenerMsgrs.size() - 1; i >= 0; i--) { try { //Listに格納したMessengerのMessenger#send() methodを使用し //ActivityへのCallbackを実装 mListenerMsgrs.get(i).send(Message.obtain(null, TEXT_UPDATE, (mValue + mCount), 0)); } catch (RemoteException e) { mListenerMsgrs.remove(i); } } mCount++; mServiceHndlr.sendEmptyMessageDelayed(TEXT_UPDATE, 3000L); break; default: super.handleMessage(msg); } } } }
AIDLを書くよりも楽にServiceとのやり取りが出来たと思います。ようはActivity、Service、それぞれ紐づけられたHandlerにお互いのMessengerを通してMessageを送信しあうというイメージですね。