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; }