2012年10月22日月曜日

別APKのServiceとAIDL通信を実装するサンプル

10月もいつの間にか終盤です。
塩崎です。
ここで白状しますが、実は僕はSEです。
プログラマです。
なので、たまにはプログラムに関する記事を書こうと思います(゜▽゜)
(塩崎)
今回紹介するのは最近流行りのAndroidのプログラムです。
Androidのアプリと言えば様々なものがありますが、今回はAIDLを用いたプロセス間通信についてざっくりサンプルコードを書いてみようと思います。
アプリの概要ですが、簡単なじゃんけんアプリです。
ただし、ActivityはGUIの処理のみで、コンピュータの手の生成や勝敗判定はActivityからパラメタを受け取ったServiceで行い、結果をActivityに返すという構成です。
目玉としては、ActivityとServiceを別のAPKにて実装すると言う点です。
それではまず、AIDLファイルのコードです。
パッケージはjp.bajitofu.masa.aidlsampleserviceです。
package jp.bajitofu.masa.aidlsampleservice;

interface IAIDLSampleService{
    //  プレイヤの手の文字列を返す
    String getStrMyHand(int inMyHand);
    //  コンピュータの手を生成し、文字列で返す
    String getStrEnmHand();
    //  勝敗を文字列で返す
    String getStrResult();
}
そして同パッケージにて上記AIDLを実装したServiceを記述します。
package jp.bajitofu.masa.aidlsampleservice;

import java.util.Random;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

/**
 * じゃんけんの手と結果を計算するサービス
 * 
 * @author masashio
 * 
 */
public class AIDLSampleService extends Service {
 // じゃんけんの手の配列
 private final String[] HAND = { "グー", "チョキ", "パー" };
 // じゃんけんの結果の配列
 private final String[] RESULT = { "あいこ!", "あなたの負け!", "あなたの勝ち!" };

 // デバッグ用
 private final String TAG = "AIDLSampleService";

 /**
  * サービスがバインドされた時にコールバックされる
  */
 @Override
 public IBinder onBind(Intent intent) {
  Log.d(TAG, "バインドされた!");
  // インテントが本物か確認(不要かも?)
  if (IAIDLSampleService.class.getName().equals(intent.getAction())) {
   // IADLSampleServiceインターフェースを実装したインスタンスを返す
   return aidlSampleServiceIf;
  }
  return null;
 }

 // IAIDLSampleServiceの実装
 IAIDLSampleService.Stub aidlSampleServiceIf = new IAIDLSampleService.Stub() {
  // プレイヤの手とコンピュータの手を持っておく
  int inMyHand, inEnmHand;

  /**
   * 結果を文字列で返す
   */
  @Override
  public String getStrResult() throws RemoteException {
   Log.d(TAG, "結果を返すよ!");
   return RESULT[(inMyHand - inEnmHand + 3) % 3];
  }

  /**
   * プレイヤの手の文字列を返す
   */
  @Override
  public String getStrMyHand(int inMyHand) throws RemoteException {
   Log.d(TAG, "プレイヤの手を返すよ!");
   this.inMyHand = inMyHand;
   return HAND[inMyHand];
  }

  /**
   * コンピュータの手を生成し、文字列で返す
   */
  @Override
  public String getStrEnmHand() throws RemoteException {
   Log.d(TAG, "コンピュータの手を返すよ!");
   inEnmHand = new Random().nextInt(2);
   return HAND[inEnmHand];
  }
 };

}
続いてActivity側です。
こちらのパッケージ名はjp.bajitofu.masa.aidlsampleclientです。
まずはXMLのレイアウトファイルです。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/rockbtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="グー"
            android:textSize="25dp" />

        <Button
            android:id="@+id/scissorsbtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="チョキ"
            android:textSize="25dp" />

        <Button
            android:id="@+id/paperbtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="パー"
            android:textSize="25dp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="あなたの手:"
            android:textSize="20dp" />

        <TextView
            android:id="@+id/myhand"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="20dp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="コンピュータの手:"
            android:textSize="20dp" />

        <TextView
            android:id="@+id/enmhand"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="20dp" />
    </LinearLayout>

    <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30dp" />

</LinearLayout>
最後に、クライアントとなるActivityのソースです。
package jp.bajitofu.masa.aidlsampleclient;

import jp.bajitofu.masa.aidlsampleservice.IAIDLSampleService;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class AIDLSample extends Activity {
 // IAIDLSampleServiceのインスタンス
 private IAIDLSampleService aidlSampleServiceIf;

 // UIパーツのインスタンス
 private Button rockBtn, scissorsBtn, paperBtn;
 private TextView myHandView, enmHandView, resultView;

 // デバッグ用
 private final String TAG = "AIDLSample";

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_aidlsample);

  // IAIDLSampleServiceをバインド
  Intent intent = new Intent(IAIDLSampleService.class.getName());
  bindService(intent, aidlSampleServiceConn, BIND_AUTO_CREATE);

  // UIパーツ取得
  rockBtn = (Button) findViewById(R.id.rockbtn);
  scissorsBtn = (Button) findViewById(R.id.scissorsbtn);
  paperBtn = (Button) findViewById(R.id.paperbtn);
  myHandView = (TextView) findViewById(R.id.myhand);
  enmHandView = (TextView) findViewById(R.id.enmhand);
  resultView = (TextView) findViewById(R.id.result);

  // ボタンにクリックリスナーを登録
  rockBtn.setOnClickListener(new myBtnClickListener());
  scissorsBtn.setOnClickListener(new myBtnClickListener());
  paperBtn.setOnClickListener(new myBtnClickListener());

 }

 @Override
 protected void onDestroy() {
  // IAIDLSampleServiceをunbind
  unbindService(aidlSampleServiceConn);

  super.onDestroy();
 }

 /**
  * クリックリスナー実装クラス
  * 
  * @author masashio
  * 
  */
 private class myBtnClickListener implements OnClickListener {
  // 手の定数
  private final int ROCK = 0, SCISSORS = 1, PAPER = 2;

  // プレイヤの手を持っておく
  int inMyHand;

  /**
   * クリックされた時の挙動
   */
  @Override
  public void onClick(View view) {
   Log.d(TAG, "クリックされたよ!");

   // 自分の手を数値化する
   switch (view.getId()) {
   case R.id.rockbtn:
    inMyHand = ROCK;
    break;
   case R.id.scissorsbtn:
    inMyHand = SCISSORS;
    break;
   case R.id.paperbtn:
    inMyHand = PAPER;
    break;
   default:
    // なんかエラー
    inMyHand = -1;
   }

   // サービスインターフェースを叩く
   try {
    myHandView.setText(aidlSampleServiceIf.getStrMyHand(inMyHand));
    enmHandView.setText(aidlSampleServiceIf.getStrEnmHand());
    resultView.setText(aidlSampleServiceIf.getStrResult());
   } catch (RemoteException e) {
    // やばいエラー
    Log.d(TAG, "RemoteException");
   } catch (NullPointerException e) {
    // サービスが死んでるとぬるぽが返る
    Log.d(TAG, "ぬるぽ(´・ω・`)");
   }
  }

 }

 /**
  * サービスと接続・切断された時の処理
  */
 private ServiceConnection aidlSampleServiceConn = new ServiceConnection() {

  /**
   * サービスに接続された
   */
  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   // サービスのインターフェースを取得する
   aidlSampleServiceIf = IAIDLSampleService.Stub.asInterface(service);
   Log.d(TAG, "インターフェース取得!");
  }

  /**
   * サービスと切断された
   */
  @Override
  public void onServiceDisconnected(ComponentName name) {
   aidlSampleServiceIf = null;
   Log.d(TAG, "サービスと切断!");
  }

 };

}
以上です。
Androidの開発環境構築してある方は、ぜひ実行してみたください(゜▽゜)
それではまた!




0 件のコメント :