Androidでプロセス間通信

AIDLによるプロセス間通信

用途
 androidで常駐サービスを立ち上げた状態で、別プロセスからサービスのメソッド
 実行したくなる場合がある。例えばgpsによる位置取得常駐サービスを常駐サービスを
 立ち上げた状態で、別プロセスからサービス内の一取得開始、停止のメソッドを実行
 したくなることがある。

対応方法
 その場合、androidでプロセス間通信(interprocess communication)を行うのだが、
 そのためにはaidl(android interface development language)でプロセス間通信用
 のインターフェースを用いる。
 aidlによって別プロセス間で通信を行うためのインターフェースを定義することができる。
 

aidlとは
 拡張子がaidlになるだけで、記述の内容はほぼjavaのインターフェースと同じだと思う
 サービス本体のaidlとcallback用のaidlの2つ一組が最低でも必要になる。メソッド呼び出
 し時に引数としてデータを渡す場合は、引数をParcelでバイト配列化してバイトストリーム
 に流せるようにする。
 
 サービスのaidlではプロセス間通信で呼び出せるメソッドを宣言する。registerCallback,
 unregisterCallbackはコールバックの登録、登録解除で必須になる。
 以下、サービスのaidlのサンプルになる

package com.example.LifeRecorder.service.gpsService;

import com.example.LifeRecorder.service.gpsService.IGpsServiceCallback;

interface IGpsService {

	oneway void registerCallback(IGpsServiceCallback callback);

	oneway void unregisterCallback(IGpsServiceCallback callback);

	void timerTask();

	void statusChange();

}

 callbackのaidlにはサービス側から呼び出されるメソッドを宣言する。
 以下、callbackのaidlのサンプルになる。

package com.example.LifeRecorder.service.gpsService;

oneway interface IGpsServiceCallback {
	void result();
}

aidlの実装
 aidlを宣言した後は、アクティビティ、サービスでaidlを実装することでプロセス間
 通信を行えるようになる。

 以下サービスでaidlを実装するサンプルになる。

private IGpsService.Stub mStub = new IGpsService.Stub() {
	private RemoteCallbackList<IGpsServiceCallback> mCallbacks = new RemoteCallbackList<IGpsServiceCallback>();
	@Override
	public void registerCallback(IGpsServiceCallback callback)
			throws RemoteException {
		Log.d(logTag, "registerCallback");
		mCallbacks.register(callback);

	}

	@Override
	public void unregisterCallback(IGpsServiceCallback callback)
			throws RemoteException {
		Log.d(logTag, "unregisterCallback");
		mCallbacks.unregister(callback);

	}

	@Override
	public void statusChange() throws RemoteException {
		Log.d(logTag, "プロセス間通信成功");
	}

	/**
	 * タイマー処理で現在の位置を保存
	 */
	@Override
	public void timerTask() throws RemoteException {
		Log.d(logTag, "timerTask");
		locationListener = new MyLocationListener();
		String provider = gpsLowMode();
		if(provider != null){
			Looper looper = Looper.getMainLooper();
			gpsLocationManager.requestLocationUpdates(provider, 10000L, 0, locationListener, looper);
			MODE = START;
		}
	}

};

stubというのはプロセス間通信の受信側で使用する抽象クラスである。javaのabstractクラスのようなもの。
ちなみに、抽象クラスは以下の3つの場面で使えるらしい。


1.あるインターフェイスを実装するクラスを複数作成したところ、どのクラスにも共通の実装が必要なことが分かって、コードを共有化したい
2.クラスが実装するべきインターフェイスは決まっていて、一部については実装方法も決まっているが、全部の実装方法までは決まっていない
3.既存のクラスに、あるインターフェイスを実装することにして、基本クラスとして使いたい

以下、アクティビティでaidlを実装するサンプルである。

private IGpsServiceCallback mCallback = new IGpsServiceCallback.Stub() {

	@Override
	public void result() throws RemoteException {

	}

};

それから、アクティブティ側でサービスとバインドする場合は以下のようになる。

private IGpsService mService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
	@Override
	public void onServiceDisconnected(ComponentName name) {
		//mService = null;
	}

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		mService = IGpsService.Stub.asInterface(service);
		try {
			mService.registerCallback(mCallback);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
};


@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	initView();

	bindService(new Intent(IGpsService.class.getName()), mServiceConnection, BIND_AUTO_CREATE);

}

これでbindSerivce実行後にServiceConnectionクラスのonServiceConnectedメソッドが呼ばれて
アクティビティ側でIGpsServiceを受け取ってサービス内のメソッドを実行できるようになる。

もしここで、onServiceConnectedが呼ばれていなかったらサービス側のクラスでonBindメソッドが呼ばれた
ときにstubを返していないかもしれない。
以下は、サービスクラスでonBindメソッドが呼ばれたときにstubを返すサンプルになる

@Override
public IBinder onBind(Intent intent) {
	Log.d(logTag, "service onBind");
	return mStub;
}