Messenger is based on AIDL. There are several key elements:
- IMessenger, this interface is same as the interface generated by specific AIDL. It has an inner class of Stub. and contains a member function: void send(Message msg)
- Handler is also a key element when talks about Messenger. Handler holds an instance that extends IMessenger.Stub (so it holds a Binder), and it's accessed by getIMessenger(), and implementation of that method is:
class Handler { IMessgener mMessenger; public IMessenger getIMessenger() { if(mMessenger == null) { mMessenger = new IMessengerImp(); } return mMessenger; } } class IMessengerImp extends IMessenger.Stub {...}
So a handler actually holds the Binder between service and its remote-process clients.
- Messenger class, the constructors and some key methods of this class:
IMessenger mTarget; public Messenger(Handler target) { // The target is the Handler as we talked about above, it holds a instance that extends IMessenger.Stub (which is both IMessenger and Binder). mTarget = target; } public Messenger(IBinder target) { mTarget = new IMessenger.Stub.asInterface(target); } public IBinder getBinder() { return mTarget.asBinder(); } public void send(Message msg) { mTarget.send(msg); // calls IMessagener.send(), which will finally calls handler.send() }
Steps to create a service and remote process client which can send message to service, and service can get the message from client:
- Create a service
public class MessengerService extends Service { public MessengerService() { } private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch(msg.what) { ... } } } private final Messenger messenger = new Messenger(new MessengerHandler()); @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } }
- Create a client
private ServiceConnection mConnection = new ServiceConnection() { private Messenger mMessenger; @Override public void onServiceConnected(ComponentName className, IBinder service) { // The service parameter is just the return value of Service.onBind(). mMessenger = new Messenger(service); Message msg = Message.obtain(...); Bundle data = new Bundle(); data.putString("msg", "XXX"); msg.setData(data); mMessenger.send(msg); } } ... and we need logic to bind to service.
Steps to create a service and remote process client, which can achieve two-way communication:
KEY:Both service and client should holds two messenger, one for receiving message (constructed with a Handler instance), the other for sending message (service gets this messenger from client by calling messge.replyTo; client construct it with a Binder which got from service when onServiceConnected() triggered).
- Create service
public class MessengerService extends Service { public MessengerService() { } private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch(msg.what) { default: Messenger clientMessenger = msg.replyTo; Message msg = Message.obtain(...); Bundle data = new Bundle(); data.putString("msg", "A reply from server"); msg.setData(data); clientMessenger.send(msg); } } } private final Messenger messenger = new Messenger(new MessengerHandler()); @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } }
- Create client
private ServiceConnection mConnection = new ServiceConnection() { private Messenger mServiceMessenger; private Messenger mClientMessenger; private MessengerHandler mHandler = new MessengerHandler(); @Override public void onServiceConnected(ComponentName className, IBinder service) { // The service parameter is just the return value of Service.onBind(). // Use two different constructor to construct Messenger instance. mServiceMessenger = new Messenger(service); mClientMessenger = new Messenger(mHandler); Message msg = Message.obtain(...); Bundle data = new Bundle(); data.putString("msg", "A request from client"); msg.setData(data); //[KEY STEP] msg.replyTo = mClientMessenger; mServiceMessenger.send(msg); } private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch(msg.what) { ... } } } }