Android IPC之Messenger详解

一.Messenger简介

       与AIDL进程间通信作用一样,Messenger是基于Message对象进行跨进程通信的,类似于Handler发送消息实现子线程和UI线程进行通信,此外,还支持记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信。
       普通的进程间通信,需要服务端自己提供AIDL接口文件,然后本地实现Stub内方法,客户端需要引入方可进行通信;
       Messenger是系统提供统一的AIDL接口文件,并且由Handler来实现Stub内方法,服务端在Handler内处理即可,客户端不需要引入,直接可以通信;

二.Messenger实现流程

a.服务端实现

       1.创建一个Handler对象,并实现handleMessage方法,用于接收来自客户端的消息并作处理;
       2.创建一个Messenger(送信人),封装Handler;
       3.用Messenger的getBinder()方法获取一个IBinder对象,通过onBind返回给客户端;
       代码如下:

public class MyService extends Service {
    private static final String TAG = MyService.class.getSimpleName();
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    private MessengerHandler mHandler = new MessengerHandler();
    private Messenger mMessenger = new Messenger(mHandler);
    private static class MessengerHandler extends Handler{

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //取出客户端的消息内容
            Bundle bundle = msg.getData();
            String clientMsg = bundle.getString("client");
            Log.i(TAG,"来自客户端的消息:"+clientMsg);
            //新建一个Message对象,作为回复客户端的对象
            Message message = Message.obtain();
            Bundle bundle1 = new Bundle();
            bundle1.putString("server","已收到消息");
            message.setData(bundle1);
            try {
                msg.replyTo.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}

       服务端接受到客户端的消息,其实与线程间通信一样,也是在handleMessage方法中进行处理;
       如果服务端需要回复客户端,则需要获取到客户端携带过来的Messenger对象(即msg.replyTo),通过msg.replyTo.send()给客户端发送信息。

b.客户端实现

       1.绑定服务,创建ServiceConnection并在其中使用IBinder将Messenger实例化;
       2.使用Messenger向服务端发送消息;
       3.接收服务端发送过来的message;
       4.解绑服务;
       实现如下:

public class MyActivity extends Activity implements View.OnClickListener {
    private static final String TAG = MyActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bindService = findViewById(R.id.bindService);
        Button unBindService = findViewById(R.id.unBindService);
        bindService.setOnClickListener(this);
        unBindService.setOnClickListener(this);
    }

    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bindService:
                Intent intent = new Intent();
                intent.setAction("com.hly.learn.server.action");
                bindService(intent,mConnection,BIND_AUTO_CREATE);
                break;
            case R.id.unBindService:
                unbindService(mConnection);
                break;
           default:
                break;
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取服务端关联的Messenger对象
            Messenger mService = new Messenger(service);
            //创建Message对象
            Message message = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putString("client","hello");
            message.setData(bundle);
            try {
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

       客户端在绑定服务之后,在ServiceConnection中通过IBinder得到Messenger对象(mService),然后用mService的send方法把Message作为形参发送给服务端。

双向通信实现

       如果服务端收到消息需要回复客户端,应该怎么实现呢?
       客户端也需要创建一个Messenger对象,接着创建一个对应的处理Handler对象,最后在onServiceConnected方法中,把Messenger对象赋值给message.replyTo,通过mService.send(message)方法发送给服务端,服务端可以通过replyTo的messenger对象给客户端发消息,从而完成双向通信,实现如下:

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //获取服务端关联的Messenger对象
        Messenger mService=new Messenger(service);
        //创建Message对象
        Message message = Message.obtain();
        Bundle bundle = new Bundle();
        bundle.putString("client","hello");
        message.setData(bundle);
        //在message中添加一个回复mReplyMessenger对象,server端可通过此来给client发消息
        message.replyTo = mReplyMessenger;
        try {
            mService.send(message);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
};

private ReplyHandler mReplyHandler = new ReplyHandler();
private Messenger mReplyMessenger = new Messenger(mGetReplyHandler);
public static class ReplyHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Bundle bundle = msg.getData();
        String serviceMsg = bundle.getString("server");
        Log.i(TAG, "来自服务端的回复:"+serviceMsg);
    }
}

       服务端在AndroidManifest.xml中对service进行配置:


          
     

       其实Messenger底层也是AIDL。客户端和服务端通讯,就是普通的AIDL,客户端实例化Stub之后,通过Stub的send方法把消息发到服务端。服务端和客户端通讯:服务端通过解析message的replyto,获得客户端的Stub,然后通过send方法发送到客户端。

三.源码分析

       从上面实例使用可以看到,在Service的onBind()方法中通过mMessenger.getBinder()来返回对应的IBinder,先看一下Messenger的源码实现:

a.Messenger.java

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

   public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

   public IBinder getBinder() {
        return mTarget.asBinder();
    }
    ......
    ......
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
}

       从Messenger的源码可以看到,主要有四个方法:
       1.提供给服务端的构造方法,需要传入Handler实例,在方法内部通过Handler返回IMessenger实例;
       2.提供给客户端的send()方法,发送message到服务端;
       3.提供给服务端在onBind()返回Binder的方法;
       4.提供给客户端在onServiceConnected()内部获取Messenger实例的方法;
       Messenger内部同时提供了服务端和客户端对应的方法,getBinder()是通过mTarget来返回的,mTarget是通过Handler的getIMessenger()来返回的,具体实现是在Handler内部,一起看一下Handler的内部对应的实现:

b.Handler.java

    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) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

c.IMessenger.aidl

oneway interface IMessenger {
    void send(in Message msg);
}

d.IMessenger.java

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";
        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        ........
        ........
        @Override 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 {
            switch (code)
                ..........
                case TRANSACTION_send: {
                    data.enforceInterface(DESCRIPTOR);
                    android.os.Message _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = android.os.Message.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.send(_arg0);
                    return true;
                }
         }
         return super.onTransact(code, data, reply, flags);
    }

    private static class Proxy implements android.os.IMessenger {
        private android.os.IBinder mRemote;
        Proxy(android.os.IBinder remote) {
            mRemote = remote;
        }
        @Override public android.os.IBinder asBinder() {
            return mRemote;
        }
        .........
       @Override public void send(android.os.Message msg) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
               if ((msg!=null)) {
                   _data.writeInt(1);
                   msg.writeToParcel(_data, 0);
               } else {
                   _data.writeInt(0);
               }
                mRemote.transact(Stub.TRANSACTION_send, _data, null, android.os.IBinder.FLAG_ONEWAY);
             } finally {
                 _data.recycle();
             }
        }
    }
        static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    public void send(android.os.Message msg) throws android.os.RemoteException;
}

       从上面可以看到,getIMessenger()返回的是MessengerImpl实例,而MessengerImpl是继承了IMessenger.Stub,实现了send()方法来接收消息,从这里可以看到,用的是跟AIDL相同的处理;

总结一下

       服务端:service的onBind()方法调用Messenger.getBinder(),最终是通过IMessenger.Stub的asBinder()返回的IBinder;
       客户端:onServiceConnected()里面调用Messenger mService=new Messenger(service)来返回Messenger对象,在构造方法内部会先通过IMessenger.Stub.asInterface(target)来创建IMessenger实例mTarget;
       交互:客户端在通过send()发送消息时,会通过mTarget的send()方法,经过binder驱动处理,会调用到IMessenger.Stub的onTransact()方法,最终会调用服务端的MessengerImpl的send()方法,继而通过Handler来sendMessage(),最后服务端的Handler来handleMessage();

四.与 AIDL 比较:

       执行 IPC 时,使用 Messenger 要比使用 AIDL 实现更加简单,因为 Messenger 会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。
       对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果服务必须执行多线程处理,则应使用 AIDL 来定义接口。

你可能感兴趣的:(Android IPC之Messenger详解)