[android] AIDLによるプロセス間通信 8


タイトルにあるとおり、Activity – Service間の通信のお話です。

andoridでは、簡単にActivityからServiceのメソッドを実行することができます。

  1. AIDLファイルにIPCのインターフェイスを記述する
  2. Serviceにインターフェイスを実装する
  3. ActivityからServiceにBindし、インターフェイスを叩く

たったこれだけです。

サンプルとして、テキストボックスに入力された値をServiceで加算するプログラムを作ってみます。
こんな感じ。
2009071812

まずはAIDLファイルを作成します。他のJavaソースと同じところに、Interfaceを記述したファイルを配置します。拡張子は.aidlですが、中身はまんまJavaです。

package jp.xfutures.android.sample;

/**
 * サービスのインターフェイス.
 */
interface ISampleService {
    /**
    * 足し算.
    */
    int add(int a, int b);
}

とりあえず、足し算をするだけ。
ファイルを配置すると、自動でgen配下にJavaファイルが生成されます。

2009071811

作成した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


コメントをする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

8 thoughts on “[android] AIDLによるプロセス間通信