プロセス間通信のMessenger
8652 ワード
1.概要
Messengerは、その名の通りメッセージングであり、異なるプロセスでMessageオブジェクトを伝達することができ、Messageに必要な入局を入れることで、データのプロセス間伝達を簡単に実現することができます.Messengerは軽量レベルのIPCスキームであり,その下位実装はAIDLである.
Messengerの使用方法は簡単で、AIDLプロセスをカプセル化し、要求を一度に1つしか処理しないため、サービス側では同期の問題を考慮する必要はありません.
2.プロセス間通信の実現
1)サービス側プロセス
まず、クライアントの接続要求を処理するためにサービス側にサービスを作成し、Handlerを作成し、Messengerオブジェクトを作成する必要があります.その後、サービスのonBindメソッドでこのMessengerオブジェクトの最下位のBinderを返すとよい.
2)クライアントプロセス
クライアントプロセスでは、まずサービス側のサービスをバインドする必要があります.バインドに成功した後、サービス側から返されたIBinderオブジェクトでMessengerを作成し、このMessengerオブジェクトを介してサービス側にMessageを送信します.また、サービス側応答クライアントが必要な場合は、サービス側のようにHandlerを作成し、新しいMessengerを作成し、このMessengerオブジェクトをMessageのreplyToパラメータを介してサーバに渡す必要があります.サーバはこのreplyToパラメータを介してクライアントに応答できます.
Messengerは、AIDL上でカプセル化されているため、比較的簡単に使用できます.次の例では、クライアントが送信したメッセージに基づいて返信し、返信の結果をクライアントに表示するクライアント送信メッセージを実装します.
3)以下にクライアントとサービスのコードを直接貼り付け、実行結果を添付します.
Service:
ここでは,異なるアプリケーション間のプロセス間通信をシミュレートするために,Clientとは異なるプロセスでサービスクラスを実行することで,異なるアプリケーション間通信と同様の効果が得られる.
manifestsファイルに追加する必要があります
実行結果は次のとおりです.
3.Messengerソースコードの浅い分析:
Messengerのソースコードにアクセスし、その構造を確認します.
Messengerクラスには、HandlerとIBinderで実装できる2つのコンストラクション関数があります.これは、プロセス間通信でMessengerクラスをインスタンス化するために使用されていた2つのコンストラクション関数でもあります.
前にMessengerの下位実装がAIDLだと言っていたので、こちらはこのIMessageがあのIBookManagerと似ているのを見て、クリックしてみると確かにそうでした
以前BookManagerクラスに定義したメソッドはaddBookとgetBookListであったが,ここではMessengerがAIDLのパッケージに加えたsendメソッドを発見した.
では、この方法はどこで実現されたのでしょうか.
これはHandlerクラスにおけるMessengerImplメソッドで実現され,これは私たちが送信したmessageがHandlerのhandleMessageに現れることを説明することができる.
最後にMessengerクラスに戻ってMessengerのもう一つの重要な方法を見てみましょう.
ここでは、MessengerクラスがHandlerまたはIBinderに転送されることによってIMessengerのインスタンスを取得し、sendメソッドを呼び出すことは、実際にはIMessengerのsendメソッドをリモートで呼び出すことになる.
ここではMessengerのメカニズムを整理する必要はありません.
最後に、先ほど実現した例の簡単な図を添付します.
Messengerは、その名の通りメッセージングであり、異なるプロセスでMessageオブジェクトを伝達することができ、Messageに必要な入局を入れることで、データのプロセス間伝達を簡単に実現することができます.Messengerは軽量レベルのIPCスキームであり,その下位実装はAIDLである.
Messengerの使用方法は簡単で、AIDLプロセスをカプセル化し、要求を一度に1つしか処理しないため、サービス側では同期の問題を考慮する必要はありません.
2.プロセス間通信の実現
1)サービス側プロセス
まず、クライアントの接続要求を処理するためにサービス側にサービスを作成し、Handlerを作成し、Messengerオブジェクトを作成する必要があります.その後、サービスのonBindメソッドでこのMessengerオブジェクトの最下位のBinderを返すとよい.
2)クライアントプロセス
クライアントプロセスでは、まずサービス側のサービスをバインドする必要があります.バインドに成功した後、サービス側から返されたIBinderオブジェクトでMessengerを作成し、このMessengerオブジェクトを介してサービス側にMessageを送信します.また、サービス側応答クライアントが必要な場合は、サービス側のようにHandlerを作成し、新しいMessengerを作成し、このMessengerオブジェクトをMessageのreplyToパラメータを介してサーバに渡す必要があります.サーバはこのreplyToパラメータを介してクライアントに応答できます.
Messengerは、AIDL上でカプセル化されているため、比較的簡単に使用できます.次の例では、クライアントが送信したメッセージに基づいて返信し、返信の結果をクライアントに表示するクライアント送信メッセージを実装します.
3)以下にクライアントとサービスのコードを直接貼り付け、実行結果を添付します.
Client:package com.pignet.messengerdemo2;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.Messenger;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends AppCompatActivity { private static TextView tvMsgFromService;
Button btnSend;
EditText etClient; private Messenger mService; private Messenger mGetReplyFromService =new Messenger(new MessengerHandler()); private static class MessengerHandler extends Handler{
@Override public void handleMessage(Message msg) { switch (msg.what){ case 1:
tvMsgFromService.setText(msg.getData().getString("reply")); break;
} super.handleMessage(msg);
}
} private ServiceConnection mConnection = new ServiceConnection() {
@Override public void onServiceConnected(ComponentName name, IBinder service) {
mService=new Messenger(service);
}
@Override public void onServiceDisconnected(ComponentName name) {
}
};
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSend= (Button) findViewById(R.id.btn_send);
etClient= (EditText) findViewById(R.id.et_client);
tvMsgFromService = (TextView) findViewById(R.id.tv_msg_from_service);
Intent intent= new Intent(MainActivity.this,MessengerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
btnSend.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
String msgFromClient;
Message msg = Message.obtain(null,0);
Bundle data = new Bundle(); if((msgFromClient=String.valueOf(etClient.getText()))==null){
Toast.makeText(MainActivity.this,"The Message is null",Toast.LENGTH_SHORT).show();
}else{
data.putString("msg", msgFromClient);
msg.setData(data);
msg.replyTo= mGetReplyFromService; try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override protected void onDestroy() {
unbindService(mConnection); super.onDestroy();
}
}
Service:
package com.pignet.messengerdemo2;import android.app.Service;import android.content.Intent;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.support.annotation.Nullable;import android.util.Log;/**
* Created by DB on 2017/7/2. */public class MessengerService extends Service { private static final String TAG="MessengerService"; private static class MessengerHandler extends Handler{
@Override public void handleMessage(Message msg) { switch (msg.what){ case 0:
Log.i(TAG, "receive msg from client: "+msg.getData().getString("msg"));
Messenger mService = msg.replyTo;
Message replyMessage = Message.obtain(null,1);
Bundle bundle = new Bundle();
bundle.putString("reply"," "+msg.getData().getString("msg")+" , ");
replyMessage.setData(bundle); try{
mService.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
} super.handleMessage(msg);
}
} private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override public IBinder onBind(Intent intent) { return mMessenger.getBinder();
}
}
ここでは,異なるアプリケーション間のプロセス間通信をシミュレートするために,Clientとは異なるプロセスでサービスクラスを実行することで,異なるアプリケーション間通信と同様の効果が得られる.
manifestsファイルに追加する必要があります
<service android:name=".MessengerService"
android:process=":romote">service>
実行結果は次のとおりです.
3.Messengerソースコードの浅い分析:
Messengerのソースコードにアクセスし、その構造を確認します.
Messengerクラスには、HandlerとIBinderで実装できる2つのコンストラクション関数があります.これは、プロセス間通信でMessengerクラスをインスタンス化するために使用されていた2つのコンストラクション関数でもあります.
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
前にMessengerの下位実装がAIDLだと言っていたので、こちらはこのIMessageがあのIBookManagerと似ているのを見て、クリックしてみると確かにそうでした
public interface IMessenger extends android.os.IInterface { /** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
android.os.IMessenger { private static final java.lang.String DESCRIPTOR = "android.os.IMessenger"; public Stub() { this.attachInterface(this, DESCRIPTOR);
} public static android.os.IMessenger asInterface(...} public android.os.IBinder asBinder() { return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags) throws android.os.RemoteException {...} private static class Proxy implements android.os.IMessenger {...} public void send(android.os.Message msg) throws android.os.RemoteException;
}
以前BookManagerクラスに定義したメソッドはaddBookとgetBookListであったが,ここではMessengerがAIDLのパッケージに加えたsendメソッドを発見した.
では、この方法はどこで実現されたのでしょうか.
private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
これはHandlerクラスにおけるMessengerImplメソッドで実現され,これは私たちが送信したmessageがHandlerのhandleMessageに現れることを説明することができる.
最後にMessengerクラスに戻ってMessengerのもう一つの重要な方法を見てみましょう.
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
ここでは、MessengerクラスがHandlerまたはIBinderに転送されることによってIMessengerのインスタンスを取得し、sendメソッドを呼び出すことは、実際にはIMessengerのsendメソッドをリモートで呼び出すことになる.
ここではMessengerのメカニズムを整理する必要はありません.
最後に、先ほど実現した例の簡単な図を添付します.