2012年2月20日月曜日

海外の結婚は、私たちにバインドしている

Androidアプリ開発メモ033:サービス その5:Messengerを使用したバインドされたサービス: ぷ~ろぐ

サービスについての以前の記事:
Androidアプリ開発メモ021:サービス
Androidアプリ開発メモ030:サービス その2
Androidアプリ開発メモ031:サービス その3:IntentService
Androidアプリ開発メモ032:サービス その4:バインドされたサービス

Messengerの利用

異なるプロセスから利用できるサービスは、Messengerを使って実装することができる。
コンポーネントクライアントがサービスと異なるプロセスの場合、コンポーネントクライアントでServiceConnection#onServiceConnected()のIBinderを使ってもサービスの参照を得ることはできない。
なのでクライアントコンポーネントがサービスのメソッドを呼び出すのではなく、MessengerとHandlerでMessageを相互にやり取りする。
具体的には以下のようにする。

  • サービスでクライアントからそれぞれのコールバックを受け取るHandlerを実装する。
  • HandlerはMessengerオブジェクト(これはHandlerへの参照となる)を作成するために使用される。
  • MessengerはサービスがonBind()からクライアントに返すIBinderを作成する。
  • クライアントはIBinderを使ってクライアントがMessageオブジェクトをサービスに送信するために使用するMessenger(サービスのハンドラのHandler参照となる)をインスタンス化する。
  • サービスはそのHandler、具体的にはhandleMessage()メソッドで、それぞれのMessagerを受け取る。

MessengerとHandler

Handlerを指すMessengerを作成し、Messengerを別のプロセスに渡すことによってプロセス間の通信をすることができる。

android.os.Messengerクラスのコンストラクタ・メソッド

public Messenger(Handler target) 与えられたHandlerを指す新しいMessengerを作成する。 
public Messenger (IBinder target)  raw IBinder からMessengerを作成する。IBinderはgetBinder()で明白に読み出されたものである。 
public IBinder getBinder() このMessengerが関連するHandlerと通信するために使うIBinderを読み出す。 
public void send(Message message) このMessengerのHandlerへMessageを送信する。 

android.os.Handlerクラスのメソッド
public void handleMessage(Message msg) サブクラスはメッセージを受けるようにこれを実装しなければならない。 

Message

MessageはMessengerからHandlerに送られるデータのクラスである。
追加の2つのintフィールド、追加のオブジェクトのフィールドがある。多くのケースでこれらの追加のフィールドは割り当てをしなくてもよい。

主なフィールド


父親の場合には裁判官の信用をする方法
public int arg1
public int arg2
public Object obj
public Messenger replyTo このメッセージへの返信が送られる任意のMessenger(?)
pulic int what 受信者がこのメッセージがなんについてであるか識別できるユーザー定義メッセージコード
public static Message obtain() 新しいMessageインスタンスをglobal poolから作成する。多くの場合、新しいオブジェクトの割り当てを回避することを許す。 
public static Message obtain(Handler h, int what, int arg1, int arg2) obtain()と同じだが、ターゲット、what、arg1、arg2メンバーをセットする。 

ApiDemoの com.example.android.apis.app.MessengerService の概要は以下。
  • Handlerを作り、
  • そのHandlerをコンストラクタの引数にしてMessengerを作り、
  • onBind()は、そのMessengerのgetBinder()の戻りのIBinderを返している

ApiDemoの com.example.android.apis.app.MessengerServiceActivity の概要は以下。
  • Handlerを作り、
  • そのHandlerをコンストラクタの引数にしてMessenger(replyTo用)を作る
  • onServiceConnected()が呼ばれると、引数のIBinderをコンストラクタの引数にしてMessenger(送信用)を作り、このMessengerでメッセージを2つ送る。1つはクライアント登録コマンドでreplyToにMessenger(replyTo用)をセットしている。もう1つは値登録コマンドでreplyToには何もセットしていない。

以下、ApiDemoのMessengerService.javaとMessengerServiceActivityのコード。コメント、import、パッケージ名など少し変えている。
 package com.example.messengerserviceexample;  import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.widget.Toast;  import java.util.ArrayList;  /**  * API Demoのコードほぼそのまま。  * コメントを削ったり、import文を削っただけ。   */ public class MessengerService extends Service {    NotificationManager mNM;   /** 登録されたクライアントのメッセンジャーを保持するリスト */   ArrayList mClients = new ArrayList();   /** クライアントにセットされた最後の値を保持する */   int mValue = 0;      /**    * サービスにクライアント登録コマンド。クライアントはサービスからコールバックを受信する。    * メッセージのreplyToフィールドはクライアントのメッセンジャーでなければならない。    * このサービスをバインドする別のアプリのためにpublicにした。    */   public static final int MSG_REGISTER_CLIENT = 1;      /**    * サービスにクライアント登録解除コマンド。クライアントはサービスからのコールバック受信を止める。    * メッセージのreplyToフィールドはクライアントのメッセンジャーでなければならない。    * このサービスをバインドする別のアプリのためにpublicにした。    */   public static final int MSG_UNREGISTER_CLIENT = 2;      /**    * サービスに値セットコマンド。新しい値を供給するためにこれはサービスに送られる。    * そして新しい値とともに登録されたクライアントどれでもサービスによって送られる。    * このサービスをバインドする別のアプリのためにpublicにした。    */   public static final int MSG_SET_VALUE = 3;      /**    * クライアントからやってくるメッセージのハンドラ    */   class IncomingHandler extends Handler {     @Override     public void handleMessage(Message msg) {       switch (msg.what) {       case MSG_REGISTER_CLIENT:         // replyToのメッセンジャーをリストに追加する         mClients.add(msg.replyTo);         Log.v("MESSENGER_SERVICE", "MSG_REGISTER_CLIENT:replyTo=" + msg.replyTo);         break;       case MSG_UNREGISTER_CLIENT:         // replyToのメッセンジャーをリストから削除する         mClients.remove(msg.replyTo);         Log.v("MESSENGER_SERVICE", "MSG_UNREGISTER_CLIENT:replyTo=" + msg.
カナダの手形法の作り方replyTo);         break;       case MSG_SET_VALUE:         // 送られてきた値を登録されているすべてのクライアントに送る         mValue = msg.arg1;         Log.v("MESSENGER_SERVICE", "MSG_SET_VALUE:replyTo=" + msg.replyTo);         for (int i = mClients.size() - 1; i >= 0; i--) {           try {             mClients.get(i).send(                 Message.obtain(null, MSG_SET_VALUE, mValue, 0));           } catch (RemoteException e) {             // クライアントが死んでいる。それをリストから除去する。             // ループの内側でしても安全なようにリストを後ろから前に通過する。             mClients.remove(i);           }         }         break;       default:         super.handleMessage(msg);       }     }   }     /**    * クライアントのために公開しているターゲット(?)、IncomingHandlerにメッセージを送る。    */   final Messenger mMessenger = new Messenger(new IncomingHandler());     @Override   public void onCreate() {     Log.v("MESSENGER_SERVICE", "onCreate()");         mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);     // 開始のノーティフィケーションを表示     showNotification();   }   @Override   public void onDestroy() {     Log.v("MESSENGER_SERVICE", "onDestroy()");         // 永続的なノーティフィケーションをキャンセル     mNM.cancel(R.string.remote_service_started);     // 停止を伝える     Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();   }     /**    * サービスにバインドするとき、サービスへメッセージを    * 送るためのメッセンジャーへのインターフェースを返す。    */   @Override   public IBinder onBind(Intent intent) {     Log.v("MESSENGER_SERVICE", "onBind()");         return mMessenger.getBinder();   }   /**    * サービスが動作している間ノーティフィケーションを表示する。    */   private void showNotification() {     // このサンプルでは、tickerとexpanded notificationで同じでテキストを使う     CharSequence text = getText(R.string.remote_service_started);     // アイコン、スクロールするテキスト、タイムスタンプをセット     Notification notification       = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis());     // ユーザーがこのノーティフィケーションを選択した場合にアクティビティをランチするPendingIntent     PendingIntent contentIntent       = PendingIntent.getActivity(this, 0,           new Intent(this, MessengerServiceActivity.class), 0);     // ノーティフィケーションパネルで表示するビューの情報をセットする     notification.setLatestEventInfo(this, getText(R.string.remote_service_label),         text, contentIntent);     // ノーティフィケーションを送る     // 我々は文字列IDを使用する。なぜならそれはユニークな数だからだ。後でキャンセルするときにそれを使用する。     mNM.notify(R.string.remote_service_started, notification);   } }
 package com.example.messengerserviceexample;  import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast;  /**  * API Demoのコードほぼそのまま。  * 元のコードはpublic static な入れ子クラスでAcitivityを実装していたが  * 入れ子じゃない外側のクラスにした。  */ public class MessengerServiceActivity extends Activity {      /** サービスと通信するためのメッセンジャー */   Messenger mService = null;   /** サービスにバインドを呼んだかを示すフラグ */   boolean mIsBound;   /** 状態を表示するためのテキストビュー */   TextView mCallbackText;      /**    * サービスからやってくるメッセージのハンドラ    */   class IncomingHandler extends Handler {     @Override     public void handleMessage(Message msg) {       switch (msg.what) {       case MessengerService.MSG_SET_VALUE:         mCallbackText.setText("Received from service: " + msg.arg1);         break;       default:         super.
何が女性にアピールして見てhandleMessage(msg);       }     }   }     /**    * クライアントがIncomingHandlerにメッセージを送るターゲット、われわれが公開した(?)    */   final Messenger mMessenger = new Messenger(new IncomingHandler());           /**    * サービスのメインインタフェースと相互に作用するためのクラス    */   private ServiceConnection mConnection = new ServiceConnection() {     public void onServiceConnected(ComponentName className,         IBinder service) {              mService = new Messenger(service);       mCallbackText.setText("Attached.");       // サービスと接続している限りサービスを監視したい       try {         Message msg = Message.obtain(null,             MessengerService.MSG_REGISTER_CLIENT);         msg.replyTo = mMessenger;         mService.send(msg);                 // 例としてある値を与える         msg = Message.obtain(null,             MessengerService.MSG_SET_VALUE, this.hashCode(), 0);         mService.send(msg);       } catch (RemoteException e) {       }              // サンプルの一環として、何が起きたかユーザーに表示する       Toast.makeText(MessengerServiceActivity.this, R.string.remote_service_connected,           Toast.LENGTH_SHORT).show();     }         public void onServiceDisconnected(ComponentName className) {       // これはサービスのコネクションが不意に切断された場合       // -- すなわちプロセスがクラッシュした場合に呼ばれる。       mService = null;       mCallbackText.setText("Disconnected.");              // サンプルの一環として、何が起きたかユーザーに表示する       Toast.makeText(MessengerServiceActivity.this, R.string.remote_service_disconnected,           Toast.LENGTH_SHORT).show();     }   };     void doBindService() {     // サービスのコネクションを確立する。明示的なクラス名を使う。     // なぜなら他のアプリケーションが私たちのコンポーネントを     // 交換できるようにできるようにする理由がありません。     bindService(new Intent(MessengerServiceActivity.this,         MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);     mIsBound = true;     mCallbackText.setText("Binding.");   }     void doUnbindService() {     if (mIsBound) {       // サービスを受信し、それゆえに登録している場合は、       // ここでは、登録を解除する時間である。       if (mService != null) {         try {           Message msg = Message.obtain(null,               MessengerService.MSG_UNREGISTER_CLIENT);           msg.replyTo = mMessenger;           mService.send(msg);         } catch (RemoteException e) {           // There is nothing special we need to do if the service           // has crashed.         }       }              // Detach our existing connection.       unbindService(mConnection);       mIsBound = false;       mCallbackText.setText("Unbinding.");     }   }     /**    * 標準的なこのアクティビティの初期化。UIをセットアップし、それから    * 何かする前にユーザーがそれを突くのを待ちます。(?)    */   @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);         setContentView(R.layout.main);         // ボタンのクリックを見張る     Button button = (Button)findViewById(R.id.bind);     button.setOnClickListener(mBindListener);     button = (Button)findViewById(R.id.unbind);     button.setOnClickListener(mUnbindListener);         mCallbackText = (TextView)findViewById(R.id.callback);     mCallbackText.setText("Not attached.");   }     private OnClickListener mBindListener = new OnClickListener() {     public void onClick(View v) {       doBindService();     }   };     private OnClickListener mUnbindListener = new OnClickListener() {     public void onClick(View v) {       doUnbindService();     }   }; }

他のプロセスからバインドを許可する場合は、AndroidManifest.xml以下のようにintent-fileter要素を加える。
        MessengerService" />     

service要素にandroid:process属性を追加すると、サービスを別プロセスにできる。
android:process属性の値は、コロンで始まる値だとサービスのプロセスがアプリケーションでプライベートとなり、小文字で始まる場合(ドットで始まる場合も?)はグローバルプロセスになる。プライベートとかグローバルとかの意味はわからないが^^;

上記のサービスにバインドするアクティビティのコード。MessengerServiceActivityとほぼ同じ。bindService()の引数のIntentを作るときの引数を文字列にしている。クラスを渡したらなんかNoClassDefFoundErrorが起きた。
プロジェクトのプロパティのプロジェクト参照でMessengerServiceをチェックし、Javaのビルド・パスのプロジェクトタブでMessengerServiceを追加。


 package com.example.messengerserviceclient;  import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast;  import com.example.messengerserviceexample.MessengerService;  /**  * 他のアプリからMessengerServiceにバインドするアクティビティ。  * API Demoのコードほぼそのまま。  * 元のコードはpublic static な入れ子クラスでAcitivityを実装していたが  * 入れ子じゃない外側のクラスにした。  */ public class MessengerServiceClient extends Activity {      /** サービスと通信するためのメッセンジャー */   Messenger mService = null;   /** サービスにバインドを呼んだかを示すフラグ */   boolean mIsBound;   /** 状態を表示するためのテキストビュー */   TextView mCallbackText;      /**    * サービスからやってくるメッセージのハンドラ    */   class IncomingHandler extends Handler {     @Override     public void handleMessage(Message msg) {       switch (msg.what) {       case MessengerService.MSG_SET_VALUE:         mCallbackText.setText("Received from service: " + msg.arg1);         break;       default:         super.handleMessage(msg);       }     }   }      /**    * クライアントがIncomingHandlerにメッセージを送るターゲット、われわれが公開した(?)    */   final Messenger mMessenger = new Messenger(new IncomingHandler());            /**    * サービスのメインインタフェースと相互に作用するためのクラス    */   private ServiceConnection mConnection = new ServiceConnection() {     public void onServiceConnected(ComponentName className,         IBinder service) {              mService = new Messenger(service);       mCallbackText.setText("Attached.");        // サービスと接続している限りサービスを監視したい       try {         Message msg = Message.obtain(null,             MessengerService.MSG_REGISTER_CLIENT);         msg.replyTo = mMessenger;         mService.send(msg);                  // 例としてある値を与える         msg = Message.obtain(null,             MessengerService.MSG_SET_VALUE, this.hashCode(), 0);         mService.send(msg);       } catch (RemoteException e) {       }              // サンプルの一環として、何が起きたかユーザーに表示する       Toast.makeText(MessengerServiceClient.this, R.string.remote_service_connected,           Toast.LENGTH_SHORT).show();     }          public void onServiceDisconnected(ComponentName className) {       // これはサービスのコネクションが不意に切断された場合       // -- すなわちプロセスがクラッシュした場合に呼ばれる。       mService = null;       mCallbackText.setText("Disconnected.");              // サンプルの一環として、何が起きたかユーザーに表示する       Toast.makeText(MessengerServiceClient.this, R.string.remote_service_disconnected,           Toast.LENGTH_SHORT).show();     }   };      void doBindService() {     // サービスのコネクションを確立する。明示的なクラス名を使う。     // なぜなら他のアプリケーションが私たちのコンポーネントを     // 交換できるようにできるようにする理由がありません。 //    bindService(new Intent(MessengerServiceClient.this,  //        com.example.messengerserviceexample.MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);     bindService(         new Intent("com.example.messengerserviceexample.MessengerService"),         mConnection, Context.BIND_AUTO_CREATE);     mIsBound = true;     mCallbackText.setText("Binding.");   }      void doUnbindService() {     if (mIsBound) {       // サービスを受信し、それゆえに登録している場合は、       // ここでは、登録を解除する時間である。       if (mService != null) {         try {           Message msg = Message.obtain(null,               MessengerService.MSG_UNREGISTER_CLIENT);           msg.replyTo = mMessenger;           mService.send(msg);         } catch (RemoteException e) {           // There is nothing special we need to do if the service           // has crashed.         }       }              // Detach our existing connection.       unbindService(mConnection);       mIsBound = false;       mCallbackText.setText("Unbinding.
");     }   }     /**    * 標準的なこのアクティビティの初期化。UIをセットアップし、それから    * 何かする前にユーザーがそれを突くのを待ちます。(?)    */   @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);         setContentView(R.layout.main);         // ボタンのクリックを見張る     Button button = (Button)findViewById(R.id.bind);     button.setOnClickListener(mBindListener);     button = (Button)findViewById(R.id.unbind);     button.setOnClickListener(mUnbindListener);         mCallbackText = (TextView)findViewById(R.id.callback);     mCallbackText.setText("Not attached.");   }     private OnClickListener mBindListener = new OnClickListener() {     public void onClick(View v) {       doBindService();     }   };     private OnClickListener mUnbindListener = new OnClickListener() {     public void onClick(View v) {       doUnbindService();     }   }; }

参考:
Android: MessengerでServiceとbindする << えふログ
Service - Messengerを使ったProcess間通信 - The blog :: Memo & Journal
サービスをアプリケーションプロセスから分離する - Kazzzの日記



These are our most popular posts:

Androidアプリ開発メモ033:サービス その5:Messengerを使用した ...

2011年10月30日... その3:IntentService Androidアプリ開発メモ032:サービス その4:バインドされた サービス ... このサービスをバインドする別のアプリのためにpublicにした。 */ public static final int ... getBinder(); } /** * サービスが動作している間ノーティフィケーションを 表示する。 */ private void ... なぜなら他のアプリケーションが私たちのコンポーネントを / / 交換できるようにできるようにする理由がありません。 bindService(new ... read more

ウェブコンポーザー白書 2011

おかげさまで合計 2618 人もの BiND ユーザーさんやウェブコンポーザー学校の生徒 さんから、 一言一句、無駄にすることはできない貴重 ... 次期BiND に. 次期BiND について. img01 . enlarged button . 現行のBiNDについて. Q.BiNDの一番 重宝してる機能や一番好きなところ ..... ●LiFE with PhotoCinema、結婚式エディション はあるので、卒業式バージョンがほしいです。 ... コレまでのソフトのパッケージなど、私 たち世代には興味深くて分かりやすいけど、もっと上の年齢層の人にも広まるといいなあ 、と個人的 ... read more

ウェブコンポーザー・アワード・2011

これからもBiND for WebLiFE*が、ウェブコンポーザーの皆さんのホームページづくりを 楽しくする、ベストパートナーでありますように! 2011年8月12日 ... 群馬県で子供、 家族、結婚式などの幸せにたずさわる写真を撮影している大川渉です。ワイフワーク として ... read more

なぜロザリオでスペイン語? - Spanish in Rosario.

スペイン語コース, スペイン語のクラス海外, スペイン浸漬, スペイン語プログラム海外, スペイン語クラスアルゼンチン, スペイン語学校アルゼンチン, ... 私達の個人的なタッチ で私たちの誇り, あなたの満足としてだけでなく、我々の学生を確保するために期待され ているものの上を超え行くことにより, ... この小さな詳細を私たちはすべての方向に創造 的に成長する官僚テープと無料でバインドされていないまま当社の優位性に取り組んで. read more

Related Posts



0 コメント:

コメントを投稿