How to : Android IPC using Messenger to a Remote Service

By | February 21, 2014

In this short tutorial we will see how to create an Android remote service and bind to it in order to send two-way inter-process-communication (IPC) messages using the Android Messenger api (simplifies the use of AIDL).

In Android there are multiple ways to do IPC, such as Intents (check IntentService), Binders (AIDL or Messenger) and ASHMEM (anonymous shared memory). I selected the Messenger for this example since it transparently queues all requests into a single thread assuring thread-safeness. If you need a multi-threaded service then you should consider the use of AIDL to define your interface (check Android bound services  – implementation example here).

Android Remote Service with IPC

If you follow the bound services guide in the Android developer site, they propose implementing a similar service to the following:

public class MessengerService extends Service {
    /** Command to the service to register client binder */
    static final int MSG_REGISTER = 1;
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 2;

    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_REGISTER:
                /*
                 * Do whatever we want with the client messenger: Messenger
                 * clientMessenger = msg.replyTo
                 */
                Toast.makeText(getApplicationContext(),
                        "Service : received client Messenger!",
                        Toast.LENGTH_SHORT).show();
                break;
            case MSG_SAY_HELLO:
                /*
                 * Do whatever we want with the client messenger: Messenger
                 * clientMessenger = msg.replyTo
                 */
                Toast.makeText(getApplicationContext(),
                        "Service : Client said hello!", Toast.LENGTH_SHORT)
                        .show();
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.Note
     * that calls to its binder are sequential!
     */
    final Messenger mServiceMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger for
     * sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT)
                .show();
        return mServiceMessenger.getBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        /*
         * You might want to start this Service on Foreground so it doesnt get
         * killed
         */
    }
}

There are 3 things noteworthy in this service : the Messenger, the Handler and the Messenger Binder. The Messenger (that abstracts the AIDL) is initialized with a message Handler – the IncomingHandler class. The IncomingHandler overrides the method handleMessage that is invoked upon receiving an IPC Android Message. The Messenger binder is returned upon binding to this service so that all communication is handled through the abstracted Messenger AIDL.

The exchanged messages are Android Messages. They are Android Parceable objects created for Handlers that provide a set of useful fields for storing the message type (what – integer), two integer arguments (arg1, arg2 – integer), an object field for sending objects (obj – Object) and a very useful field to store the sender Messenger (replyTo – Messenger). Both what and arg fields are arbitrarily chosen integers decided by the developer. Unless you are using Parceable framework classes of Android (not developer implemented), the obj field should always be null for IPC.

Android IPC Client

Now, for portability purposes, instead of following the same step of the Android guide – which creates an Activity – we will create a Connector class (ServiceConnector) that can be instantiated from wherever we want (as long as we have access to Context). Also, this class shall have its own Messenger so that the Service can reply to messages :

public class ServiceConnector {
    /** Command to the service to register client binder */
    static final int MSG_REGISTER = 1;
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 2;

    /** Messenger for sending messages to the service. */
    Messenger mServiceMessenger = null;
    /** Messenger for receiving messages from the service. */
    Messenger mClientMessenger = null;

    /**
     * Target we publish for clients to send messages to IncomingHandler. Note
     * that calls to its binder are sequential!
     */
    private final IncomingHandler handler;

    /**
     * Handler thread to avoid running on the main thread (UI)
     */
    private final HandlerThread handlerThread;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /** Context of the activity from which this connector was launched */
    private Context mCtx;

    /**
     * Handler of incoming messages from service.
     */
    class IncomingHandler extends Handler {

        public IncomingHandler(HandlerThread thr) {
            super(thr.getLooper());
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_SAY_HELLO:
                Toast.makeText(mCtx.getApplicationContext(),
                        "Client : Service said hello!", Toast.LENGTH_SHORT)
                        .show();
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service. We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mServiceMessenger = new Messenger(service);

            // Now that we have the service messenger, lets send our messenger
            Message msg = Message.obtain(null, MSG_REGISTER, 0, 0);
            msg.replyTo = mClientMessenger;

            /*
             * In case we would want to send extra data, we could use Bundles:
             * Bundle b = new Bundle(); b.putString("key", "hello world");
             * msg.setData(b);
             */

            try {
                mServiceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mServiceMessenger = null;
            mBound = false;
        }
    };

    public ServiceConnector(Context ctx) {
        mCtx = ctx;
        handlerThread = new HandlerThread("IPChandlerThread");
        handlerThread.start();
        handler = new IncomingHandler(handlerThread);
        mClientMessenger = new Messenger(handler);
    }

    /**
     * Method used for binding with the service
     */
    public boolean bindService() {
        /*
         * Note that this is an implicit Intent that must be defined in the
         * Android Manifest.
         */
        Intent i = new Intent("com.aknahs.myservice.ACTION_BIND");

        return mCtx.getApplicationContext().bindService(i, mConnection,
                Context.BIND_AUTO_CREATE);
    }

    public void unbindService() {
        if (mBound) {
            mCtx.getApplicationContext().unbindService(mConnection);
            mBound = false;
        }
    }

    public void sayHello() {
        if (!mBound)
            return;

        // Create and send a message to the service, using a supported 'what'
        // value
        Message msg = Message.obtain(null, MSG_SAY_HELLO, 0, 0);
        try {
            mServiceMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

Note that in the ServiceConnector there is also a Message Handler (IncomingHandler) and a Messenger for receiving the messages from the Service. Unlike the Service side, here we opted for attaching the Handler to an HandlerThread. Why? Because, for example, if we instantiate this connector on the MainActivity, its Handler will be running on the main thread, possibly slowing down the UI. Or in case we later want to implement a synchronous communication behaviour, it is always good that the Handler runs in a different thread that does not get blocked while waiting for a Message.

There are 2 extra things noteworthy in this connector : the ServiceConnection field and the bindService method. The ServiceConnection provides two methods that are invoked on Service connection (onServiceConnected) and disconnection (onServiceDisconnected). In order to store the client Messenger on the Service side, it is possible to respond to the service connection with a Message that contains our client Messenger (on the replyTo field of the Message). For example, in case you want to actually answer back, in the service side you need to retrieve the client messenger from the message (in the handleMessage method):

Messenger mess = msg.replyTo; //retrieves messenger from the message

And then use it for sending a message back. E.g:

Message m = new Message();                  //create the message to send back to the client
Bundle b = new Bundle();                    //Just to show how to send other objects with it
b.putString(“messengerName”, “messenger1″); //this could be any parceable object
m.setData(b);                               //adds the bundle to the message
mess.send(m);                               //sends message

Remote Service Manifest

Regarding the bindService method, you might notice that the Intent that we used is a implicit Intent. Why? Because it simplifies the process of identifying the Service to which we want to bind. While binding, Android will try to match this intent to the intent-filters of other applications. So lets conclude by looking at our Service declaration in the AndroidManifest.xml (inside the <application>):

 
 <service
     android:name="com.aknahs.myservice.MessengerService"
     android:enabled="true"
     android:exported="true" >
          <intent-filter>
               <action android:name="com.aknahs.myservice.ACTION_BIND">
          </intent-filter>
 </service>

We set the exported field to true to allow other applications to invoke our service. And we declare the Intent we used to bind to the service. Note that this opens some security holes as any application can connect to your service. So in order to grant/deny access to your service you can also define the android:permission field of your service.

There might be the case that once your application attempts to bind to the remote service it will lack permissions. Although I haven’t checked in detail the reason for this, it might be because some the service application permissions need to also be declared in the binding application. In my case I needed to add the android.permission.INTERNET.

Limitations?

Also note that, the Android BinderProxy that the Messenger uses to send data between processes has small size limits and you can end up with an android.os.TransactionTooLargeException (page suggests limit of 1Mb! Although in some forums they claim 1MB). Even by partitioning the messages in smaller chunks, you might get a JavaBinder exception “FAILED BINDER TRANSACTION” because the smaller chunks if sent in a serialized manner without waiting will eventually pass the transaction limits. So the suggestion is either to use a ContentProvider or a ParcelFileDescriptor. Ultimately you can also try sockets.


Extra: Adding Synchronous behavior

What if you would want to wait for an answer to your IPC Message?

Java objects allow you to synchronize on them and perform wait(). When you send a Message you can store it in a Map with its message id as a key and, after sending it, call its wait() method so that the current thread blocks (make sure this thread is not your UI thread!). If you implemented the HandlerThread approach that we discussed, the Handler will run on a different thread, receive the IPC message and store it. You can search the sent message Map for the ID and invoke the notify() method on the retrieved message. The previously blocked thread will wake and retrieve the stored incoming Message.

Is this a good approach?

No! Why? Well, ignoring the possible memory waste of storing sent messages when you could just store its message identifier (assuming it extends Object), there is a property about Messages and Handlers that makes this solution buggy. In Android, Messages received by an Handler are recycled from a Message pool. If one of these message is stored and later accessed it will have an inconsistent state. A possible solution would be to store only the Message payload (e.g. the setData() bundle), a messade ID or even a fresh copy of the Message.

Extra: Other questions

Other than the comments in the bottom of this post you are also welcome to drop StackOverflow urls. I will try to keep a record here:

Passing a bundle in a message
My service will not die even though my code says it is dead

Extra: Show me the code

This tutorial was extracted from a project I was working on – Angel -, a Xposed demo that intercepts images and text from apps and sends them to a remote server that displays them in real time for parental control purposes.

The service message handler in this project is : service handler, and the code that connects to the service : Service Connector.

I apologize that the Service Connector might be complicated to understand as it also performs the routing of MQTT messages but its the simplest working example I have atm.

Hope you enjoyed this tutorial, feel free to post any questions. Happy Hacking!

21 thoughts on “How to : Android IPC using Messenger to a Remote Service

  1. amanjot

    mClientMessenger When this variable will be used.and how service is sending messages and how they are received

    Reply
    1. admin Post author

      Hi amanjot,

      For the simplicity of the example the service side only receives messages and presents a toast message.

      In case you want to actually answer back, in the service side you need to retrieve the client messenger from the message (in the handleMessage method):

      Messenger mess = msg.replyTo; //retrieves messenger from the message
      

      And then use it for sending a message back. E.g:

      Message m = new Message(); //create the message to send back to the client
      Bundle b = new Bundle(); //Just to show how to send other objects with it
      b.putString("messengerName", "messenger1"); //this could be any parceable object
      m.setData(b); //adds the bundle to the message
      mess.send(m); //sends message
      

      Hope this helps.

      Reply
  2. John Smith

    When I attempt to compile, I’m getting a crash: ” Caused by: java.lang.InstantiationException: can’t instantiate class com.example.MainActivity; no empty constructor” Any ideas why this is occurring?

    Reply
    1. admin Post author

      I don’t know your code but it is probable that you just need to add a empty constructor to com.example.MainActivity:

      public MainActivity(){super("MainActivity")}
      

      If you created an Android project in Eclipse with an empty Activity this should suffice.

      Reply
    1. admin Post author

      Sorry, I wrote this based on a way more complex piece of code I implemented. When I have some time I will write it separately and share. Thanks for the suggestion!

      Reply
  3. Conor

    Hey, ive been looking at this the last evening or two. I am making a Service that is for GPS. Im just wondering how do I get an activity to poll the service? After the service fires off onLocationChanged I can send a message with the co-ordinates. This will go to the ServiceConnector class (or the activity if i just put the ServiceConnector code in an activity). But either way how can I get that information to the activity from the handler?

    Reply
    1. admin Post author

      Hi Conor, hope its not too late.

      If you notice, the client code has a service bound callback function :

      public void onServiceConnected(ComponentName className, IBinder service) {
                  mServiceMessenger = new Messenger(service);
                  Message msg = Message.obtain(null, MSG_REGISTER, 0, 0);
                  msg.replyTo = mClientMessenger;
                  mServiceMessenger.send(msg);
                  mBound = true;
      }
      

      In this method, it creates a Messenger to send a message to the service.
      This message has a msg.replyTo (= to the client Messenger) and a msg.what (=MSG_REGISTER) fields.

      On the service side, the handler handleMessage receives this message and can keep a reference to the client Messenger by acessing the msg.replyTo field. Check my reply to amanjot.

      If the client is bound and there is a reference to this Messenger (you have to assure the client establishes this connection), then on your onLocationChanged, you can build a Message with a new what field (e.g. MSG_LOCATION = 3), add the coordinates to a bundle (probably as Long values) and send to the client side.

      When the client receives this message on its handleMessage, since it is running on a diferent thread from the Activity you probably can’t access the UI directly (if this is what you need). An option can be to use the runOnUiThread method to post results to the UI.

      Hope this was helpful.
      Cheers!

      Reply
  4. stiga holmen (@stiga_holmen)

    Hi! Thanks for the great article. I’ve implemented a ServiceConnector class like the one you described, plus a setReceiver(Receiver) so that when I get the response from the service I call the callback of the activity implementing the ResultReceiver class and registered with the setReceiver(Receiver) method.

    My problem is: how can I handle multiple requests in the service from different activities and then answer back to each one asynchronously ? Thanks in advance!

    Reply
    1. admin Post author

      Hi and thanks!

      I assume: 1) your activities belong to the same app. 2) activities share the same ServiceConnector. If it is not the case just tell.

      This is just one of the multiple possible solutions:
      Create a method on the ServiceConnector to send messages that receives the Activity (Activity.this).
      When you are about to send a message, increment a static integer unique identifier (make sure its access is sync) and add it to the Message to be sent.
      Additionally, keep an HashMap that maps this identifier to the respective Activity.
      Send the message to the Service.

      On the Service, just make sure your reply contains the same identifier and send it in the Message arg1 reply.
      Once the reply is received on the ServiceConnector, perform a get on the HashMap to retrieve the correct Activity to notify.

      Untested code:

      //On the ServiceConnector------------
      private static Integer id;
      private HashMap<Integer, Activity> activities = new HashMap<Integer, Activity>();
      
      public void sendMessage(Activity a, Int what, String txt){
          if(!mBound) return;
      
          Integer msgID;
          synchronized(id){
              id++;
              msgId = id;
          }
      
          activities.put(msgId, a); 
          Message msg = Message.obtain(null, what, id, 0);
          Bundle b = new Bundle();
          b.putString("txt", txt);
          msg.setData(b);
          mServiceMessenger.send(msg);
      }
      
      //On the MessengerService Incoming Handler------------
      public void handleMessage(Message msg){
          //Assuming you are executing something computational intensive
          new Thread(new Runnable() {
              String txt;
              Messenger replyTo;
              Integer id;
      
              @Override
              public void run() {
                  //Perform your stuff here
                  Message replymsg = Message.obtain(null, what, id, 0);
                  replyTo.send(replymsg);
              }
              public Runnable setup(Message m){
                  //Set txt
                  replyTo = m.replyTo;
                  id = m.arg1; 
                  return this;
              }
          }.setup(msg)).start();
      }
      
      //On the ServiceConnector IncomingHandler------------
      public void handleMessage(Message msg){
          Activity replyActivity = activities.get(msg.arg1);
          //Notify your activity
      }
      

      Hope this is helpful 🙂 Good luck!

      Reply
  5. stiga holmen (@stiga_holmen)

    Hi thanks for the reply. This is my situation right now:
    – App1 with different activities
    – App2 with different activities
    – The Service (in another App)
    – a ClientResultReceiver class that extends ResultReceiver with a Receiver interface inside, with a callback onReceiveResult(..). This callback is implemented in each activity.

    This is the workflow I was thinking about
    # App1 activity1 and activity2 onCreate() gets a ServiceConnector instance
    # they bind to the service
    # they register themselves with setReceiver(this) to be called back from ServiceConnector instance onHandleMessage(..) method with a send(..)
    # activity1, activity2 onReceiveResult(..) is fired

    So until now this is what happens:
    # activity1, ativity2 onClick() or some other operation call SendMessage()
    # the service answers back (not asynchronously, but it’s OK for now)
    # ServiceConnector instance onHandleMessage(..) gets the message and sends it back through the ClientResultReceiver reference
    # the onReceiveResult(..) method in the activity is fired, BUT ONLY IN THE LAST ACTIVITY used. All the messages from the Service are delivered to this last activity…

    How I can avoid this? How can I call the onReceiveResult(..) in each activity that sent a message?

    Hope I explained my situation clear 😀
    Thanks in advance!

    Reply
  6. stiga holmen (@stiga_holmen)

    Sorry for the previous post, I had a stupid logging error and I didn’t see the correct responses from the server..all work as supposed, I just need to implement Threads on the service now.

    Thanks again for your support! =)

    Reply
    1. admin Post author

      Hi,

      For some reason I didnt get notified of your previous comments.
      I am glad you were able to figure it out! 🙂

      Cheers

      Reply
  7. Md Maidul Islam

    Hi ,
    Could you please send me the whole code of messenger. Please send me Help me

    Reply
  8. Md Maidul Islam

    Hi , Great Blog. Keep it Up.
    I want to create chat application . How can I do that Suppose I create two emulator and communicate each other. Then Next steps is How can I chat on two mobiles. Please send me the sample code.
    I am looking for your positive reply.

    Kind Regards,
    Md Maidul Islam

    Reply

Leave a Reply