[android] AIDLによるプロセス間通信
タイトルにあるとおり、Activity – Service間の通信のお話です。
andoridでは、簡単にActivityからServiceのメソッドを実行することができます。
- AIDLファイルにIPCのインターフェイスを記述する
- Serviceにインターフェイスを実装する
- ActivityからServiceにBindし、インターフェイスを叩く
たったこれだけです。
サンプルとして、テキストボックスに入力された値をServiceで加算するプログラムを作ってみます。
こんな感じ。

まずはAIDLファイルを作成します。他のJavaソースと同じところに、Interfaceを記述したファイルを配置します。拡張子は.aidlですが、中身はまんまJavaです。
package jp.xfutures.android.sample;
/**
* サービスのインターフェイス.
*/
interface ISampleService {
/**
* 足し算.
*/
int add(int a, int b);
}
とりあえず、足し算をするだけ。
ファイルを配置すると、自動でgen配下にJavaファイルが生成されます。

作成したInterfaceを実装するServiceを作成します。
Service.onBindをオーバーライドしてInterfaceを実装したクラスのインスタンスを返します。
package jp.xfutures.android.sample;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class SampleService extends Service {
@Override
public IBinder onBind(Intent intent) {
if(ISampleService.class.getName().equals(intent.getAction())){
// ISampleServiceの実装クラスのインスタンスを返す
return sampleSerciceIf;
}
return null;
}
/**
* ISampleServiceの実装.
*/
private ISampleService.Stub sampleSerciceIf = new ISampleService.Stub(){
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
}
次は呼び元のActivityを作ります。
bindService()の第2引数にはServiceConnectionの実装を指定します。Bind/Unbind時にこのインスタンスのonServiceConnected()/onServiceDisconnected()が呼ばれます。onServiceConnected()でServiceとのInterfaceを取得できますので、そいつを使ってServiceとやりとりします。
第3引数にBIND_AUTO_CREATEを指定すると、Serviceが未起動の時に起動してくれます。
アプリ終了前にUnbindServiceを忘れずに。
package jp.xfutures.android.sample;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.TextView;
public class SampleActivity extends Activity {
/** SampleServiceのインターフェイス. */
private ISampleService sampleServiceIf;
/**
* Activityができたよ.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// SampleSericeにbindする
Intent intent = new Intent(ISampleService.class.getName());
bindService(
intent,
sampleServiceConn,
BIND_AUTO_CREATE
);
// EditTextにonKeyListenerを設定する
EditText editA = (EditText)findViewById(R.id.EditText01);
editA.setOnKeyListener(new EditListener());
EditText editB = (EditText)findViewById(R.id.EditText02);
editB.setOnKeyListener(new EditListener());
}
/**
* Activityがヤラれたよ.
*/
@Override
protected void onDestroy() {
super.onDestroy();
// SampleServiceからunbindする
unbindService(sampleServiceConn);
}
/**
* キー入力されたらアレするやつ.
*/
private class EditListener implements OnKeyListener{
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
EditText editA = (EditText)findViewById(R.id.EditText01);
EditText editB = (EditText)findViewById(R.id.EditText02);
String rslt;
try{
int a = Integer.parseInt(editA.getText().toString());
int b = Integer.parseInt(editB.getText().toString());
// サービスIFを叩く
int sum = sampleServiceIf.add(a, b);
rslt = String.valueOf(sum);
}
catch(NumberFormatException e){
rslt = "数値で";
}
catch(RemoteException e){
// サービスで例外が発生したらRemoteExceptionが飛ぶはず
rslt = "ダメっ";
}
TextView sumText = (TextView)findViewById(R.id.TextView02);
sumText.setText(String.valueOf(rslt));
return false;
}
}
/**
* Serviceに接続・切断したときにアレするやつ.
*/
private ServiceConnection sampleServiceConn = new ServiceConnection(){
/**
* サービスに接続したときに叩かれるメソッド.
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// サービスIFを取得する
// このIFを使ってサービスとやり取りする
sampleServiceIf = ISampleService.Stub.asInterface(service);
}
/**
* サービスから切断したときに叩かれるメソッド.
*/
@Override
public void onServiceDisconnected(ComponentName name) {
sampleServiceIf = null;
}
};
}
最後になっていまいましたが、AndroidManifest.xmlにInterfaceを記述してあげます。
こいつを記述しないと動きませんので、忘れないように。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.xfutures.android.sample"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".SampleActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="SampleService">
<intent-filter>
<!-- これを忘れないように!! -->
<action android:name="jp.xfutures.android.sample.ISampleService"></action>
</intent-filter>
</service>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
まとめ。
AndroidSample2009071801.zip
参考にさせていただいています。
ご存知でしたら教えていただきたいのですが、
androidのサービスをSDKのような形で提供して、
それをActivityを含むアプリケーションから実行できるように
したいと考えているのですが、可能なのでしょうか?
@master
できると思いますよ。
サービス側で作ったAIDLファイルを利用者に配布すれば良いかと。
回答ありがとうございます。
返信遅くなりました。
なるほど、AIDLファイルを提供するわけですね。
頑張ってみます!
いつも参考にさせていただいております。
一つ質問なのですが、Eclipse上で開発する際、AIDLを持ったサービスと、AIDLファイルを利用するActivityを別プロジェクトとして開発する場合、AndroidManifest.xmlにはどのように記載すればよいのでしょうか?
@hanzo
プロジェクトが異なっていても、AndroidManifestの記述内容は同じですよ。
こちらにサンプルをあげましたので、参考にしてみてください。
サンプル有難う御座います。
早速試してみて、動くこと確認できました?。