Android Messenger分析

6954 ワード

ソースコードを直接見てみましょう.
/**
 * Reference to a Handler, which others can use to send messages to it.
 * This allows for the implementation of message-based communication across
 * processes, by creating a Messenger pointing to a Handler in one process,
 * and handing that Messenger to another process.
 */
public final class Messenger implements Parcelable {
    private final IMessenger mTarget;

    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
Messengerは、Handlerを指し、他の人はHandlerにメッセージを送信するために使用することができる.メッセージングは、メッセージキューに基づくプロセス間通信を実現し、1つのプロセスでHandlerを指すメッセージングを作成し、他のプロセスがこのプロセスにメッセージを送信できるように、メッセージングを他のプロセスに返す.Messengerの内部には、Messengerのコンストラクション関数でHandler内のIMessengerを指すIMessengerインタフェースポインタがあり、Handlerを指すポインタが保存されます.
HandlerがIMessengerインタフェースの実装をどのように返すかを見てみましょう.
    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            Handler.this.sendMessage(msg);
        }
    }
getIMessengerメソッドの内部でメッセージキューがロックされているのを見ました.メッセージキューに1つのメッセージングのみがあることを確認するためです.これは、単一の例を実装する場合にもロックされるのと同じです.IMessengerインタフェースはプライベート内部クラスMessengerImplを指し、sendメソッドの内部でHandlerを使用する.this.sendMessageメソッド.
    /**
     * Send a Message to this Messenger's Handler.
     * 
     * @param message The Message to send.  Usually retrieved through
     * {@link Message#obtain() Message.obtain()}.
     * 
     * @throws RemoteException Throws DeadObjectException if the target
     * Handler no longer exists.
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
は、IMessengerインタフェースのsendメソッドを呼び出すMessengerのsendメソッドを再び見て、最終的にはHandlerのsendMessageメソッドを呼び出す.
サーバとしてのサービスからMessengerを呼び出したクライアントに返すにはどうすればいいのでしょうか.
	/**
	 * When binding to the service, we return an interface to our messenger for
	 * sending messages to the service.
	 */
	@Override
	public IBinder onBind(Intent intent) {
		return mMessenger.getBinder();
	}
Android developerの公式サイトを見てMessengerの例について
クライアントがサーバにバインドされている間にMessengerに戻ります.getBinder()の結果.次のように実現されます.
    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     * 
     * @return Returns the IBinder backing this Messenger.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }
のmTargetはIMessengerインタフェースのasBinder()メソッドであり、IMessenger.を実際に呼び出す.Stub.asBinderメソッド.
クライアントがサーバに接続されると、Messengerへの参照が確立されます.
		public void onServiceConnected(ComponentName className, IBinder service) {
			// This is called when the connection with the service has been
			// established, giving us the service object we can use to
			// interact with the service. We are communicating with our
			// service through an IDL interface, so get a client-side
			// representation of that from the raw service object.
			mService = new Messenger(service);
			mCallbackText.setText("Attached.");

			// We want to monitor the service for as long as we are
			// connected to it.
			try {
				Message msg = Message.obtain(null,
						MessengerService.MSG_REGISTER_CLIENT);
				msg.replyTo = mMessenger;
				mService.send(msg);

				// Give it some value as an example.
				msg = Message.obtain(null, MessengerService.MSG_SET_VALUE,
						this.hashCode(), 0);
				mService.send(msg);
			} catch (RemoteException e) {
				// In this case the service has crashed before we could even
				// do anything with it; we can count on soon being
				// disconnected (and then reconnected if it can be restarted)
				// so there is no need to do anything here.
			}

			// As part of the sample, tell the user what happened.
			Toast.makeText(Binding.this, R.string.remote_service_connected,
					Toast.LENGTH_SHORT).show();
		}
Messengerを呼び出す別の構築方法:
    /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
IBinderインタフェースをIMessengerインタフェースに変換し、クライアントのコードにサーバのMessengerを参照し、その後、サーバに何かメッセージを送りたいと思ってメッセージを送ります.
では、サーバはどのようにクライアントにメッセージを送信しますか?クライアントだけがサーバにコマンドを送るわけにはいかないでしょう.双方向通信こそ肝心だ.
				Message msg = Message.obtain(null,
						MessengerService.MSG_REGISTER_CLIENT);
				msg.replyTo = mMessenger;
				mService.send(msg);
クライアントがサーバに送信メッセージにはmsgが指定されている.replayTo=mMessenger;
    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;
メッセージは、Handlerでメッセージが受信された後、参照されたメッセージを呼び出してメッセージを送信することができるように、どの送信者によって送信されるかを指定することができる.
	/**
	 * Handler of incoming messages from clients.
	 */
	class IncomingHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_REGISTER_CLIENT:
				mClients.add(msg.replyTo);
				break;
			case MSG_UNREGISTER_CLIENT:
				mClients.remove(msg.replyTo);
				break;
			case MSG_SET_VALUE:
				mValue = msg.arg1;
				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) {
						// The client is dead. Remove it from the list;
						// we are going through the list from back to front
						// so this is safe to do inside the loop.
						mClients.remove(i);
					}
				}
				break;
			default:
				super.handleMessage(msg);
			}
		}
	}
は、例えば、上記のサーバのHandlerにおいて、自分のメッセージングが送信したメッセージを受信した後、メッセージに参照されたクライアントのメッセージングの参照を1つのセットに保存することで、クライアントのメッセージングを取得し、そのsendメソッドを呼び出すだけでクライアントのコードを呼び出すことができる.これにより双方向通信が実現される.
これにより、サーバは、ブロードキャスト、マルチキャスト、ユニキャストメッセージの機能を実現することができる.
MessengerのインタフェースIMessengerは実際にAIDLによって作られています.
ファイル:frameworks/base/core/java/android/os/IMessenger.aidlはHandlerがスレッド間の通信だけでなくプロセス間の通信にも利用できるように見えます!Handler,Looper,MessageQueue,Messageの関係は深く理解する必要がある!