プロセス間通信の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)以下にクライアントとサービスのコードを直接貼り付け、実行結果を添付します.
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のメカニズムを整理する必要はありません.
最後に、先ほど実現した例の簡単な図を添付します.