博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

跨进程通信之Messenger

Posted on 2017-07-02 10:35  行歌  阅读(655)  评论(0编辑  收藏  举报
1.简介
Messenger,顾名思义即为信使,通过它可以在不同进程中传递Message对象,通过在Message中放入我们需要的入局,就可以轻松实现数据的跨进程传递了。Messenger是一种轻量级的IPC方案,其底层实现是AIDL。
Messenger的使用方法很简单,它对AIDL进程了封装,并且由于它一次只处理一个请求,因此在服务端我们不需要考虑同步的问题。
 
2.实现跨进程通信
1)服务端进程
首先我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象。然后在Service的onBind方法中返回这Messenger对象底层的Binder即可。
2)客户端进程
客户端进程中,首先需要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,并通过这个Messenger对象向服务端发送Message。此外,如果需要服务端响应客户端,我们就需要像服务端那样创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务器,服务器就可以通过这个replyTo参数回应客户端了。
 
     Messenger由于是在AIDL上进行了封装,其使用过程相对比较简单,下面的示例实现了客户端发送消息给服务端,服务端会根据客户端发送的消息予以回复并将回复的结果显示在客户端上。
3)下面直接贴上client和service的代码,最后附上运行结果。
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();
    }
}

 

 
这里为了模拟不同应用间的跨进程通信,将Service类运行在了与Client不同的进程中,这样就可以实现和不同应用间通信一样的效果。
所以我们需要在manifests文件中加入:
<service android:name=".MessengerService"
    android:process=":romote">
</service>

运行结果如下

 

 
 
3.Messenger源码浅析:
     进入到Messenger源码后,查看它的结构
Messenger类有两个构造函数,分别是可以用Handler和IBinder实现,这也是我们之前实现跨进程中通信中实例化Messenger类中已经用到的两种构造函数。
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的机制理清了。

最后附上刚才实现的例子的一个简图: