Interprocess Communication is the communication of threads across process boundaries. This type of communication is supported through the binder framework in Android. In the article on Services earlier, we discussed Bound Services that has a client-server interface. A bound service is the server which allows clients (components such as activities) to bind to the Service and then send requests and receive responses. The code we discussed works across threads in the same process but will fail in the case of remote services where the Service is running in a different process altogether.
Using a Messenger
If we want this interface to work across different processes, we can use Messenger
which’ll allow messages to be passed across process boundaries between client (client component) and server (service). With this technique we can perform interprocess communication (IPC) without the need to use AIDL directly.
What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.
Note: A Messenger is coupled with a Handler. Hence, executing all the tasks (passing messages) is sequential by design as the messages are processed on a single thread by the Handler to which it belongs whereas AIDL can execute tasks concurrently on binder threads.
Following is the list of steps involved in this process:
- The Service should implement (contain) a Handler instance that receives a callback (using
handleMessage()
for instance) for each call from the client. - The Handler is used to create a
Messenger
object which in itself is actually a reference to the Handler. - The Messenger creates an
IBinder
(viagetBinder()
) that the Service returns to clients fromonBind()
. - Clients use the
IBinder
object in itsServiceConnection
implementation to instantiate theMessenger
object that references the Service’s Handler which the client uses to sendMessage
(withsend()
for instance) objects to the Service’s Handler. So the Service receives the messages in itsHandler.handleMessage()
.
At a higher level the flow is basically like this:
- The Service passes a Messenger reference to the client processes.
- Using that reference, the client sends messages to the server process as many times as it wants to.
- This entire passing/communication mechanism takes place through the Binder framework.
Time to get dirty with some code! Here’s a simple Service class example that uses the Messenger interface:
public class MessengerService extends Service { private String TAG = "MessengerService"; // Message codes to check against Message.what // // Message.what is a User-defined message code so // that the recipient can identify what the message is about. static final int MSG_SAY_HELLO = 1; // Messenger object used by clients to send messages to IncomingHandler Messenger mMessenger = new Messenger(new TestHandler()); // Incoming messages Handler class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Bundle bundle = msg.getData(); String hello = (String) bundle.get("hello"); Toast.makeText(getApplicationContext(), hello, Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } public MessengerService() { } @Override public void onCreate() { Log.d(TAG, "onCreate called"); } /* Return our Messenger interface for sending messages to the service by the clients. */ @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind done"); return mMessenger.getBinder(); } @Override public boolean onUnbind(Intent intent) { return false; } }
Now in the client all you need to do is create a Messenger
based on the IBinder
received in the ServiceConnection
implementation and send messages using send()
. So here’s an example:
boolean isBound = false; Messenger mMessenger; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isBound = true; // Create the Messenger object mMessenger = new Messenger(service); // Create a Message // Note the usage of MSG_SAY_HELLO as the what value Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); // Create a bundle with the data Bundle bundle = new Bundle(); bundle.putString("hello", "world"); // Set the bundle data to the Message msg.setData(bundle); // Send the Message to the Service (in another process) try { mMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { // unbind or process might have crashes mMessenger = null; isBound = false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent intent = new Intent(this, MessengerService.class); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); }
A client component binds to a Service by calling bindService()
which leads to calling the onBind()
method that returns an IBinder
for interacting with the Service. This binding process is asynchronous hence bindService()
returns immediately and won’t return the IBinder
to the client. To receive IBinder
we’ll have to code a ServiceConnection
implementation which’ll receive the IBinder
in its onServiceConnected()
callback method.
Note: Only Activities, Services and Content Providers can bind to a Service. Broadcast receivers cannot do that.
So the entire binding process is implementing a ServiceConnection
, binding using bindService()
and create a Messenger object out of the IBinder
received in onServiceConnected()
so that messages can be sent to the Service. When the client component is destroyed, it’ll automatically unbind from the Service but it is always recommended to unbind using unbindService()
when you’re done interacting with the Service so that it can shutdown properly while not being used.
Make sure to have this entry in your manifest in order for the Service to work properly in a different process:
<service android:name="com.example.app.MessengerService" android:process=":my_process"> </service>
So we just saw how to create a Messenger object out of the IBinder received and then send messages to the Service. Wow! We just learnt how to send messages across process boundaries in Android. As you might have noticed, there are no methods to call on the Service by clients but the clients just deliver messages to the Service’s Handler implementation.
Service to Client communication
We saw how to send messages from the Client to the Service, but you can also send messages the other way round leading to a two-way communication/messaging. In order to do this you’ll basically have to create a Messenger object in the Client wrapping a Handler just like we did in the Service class and then send that to our Service in a Message using the replyTo
field. Then you can save the reference to the Messenger in your Service and send()
Message objects just like we did from Client to Service in our ServiceConnection implementation.
It’s pretty simple but if you’re still confused then there’s some code sample available here and here.
Messenger vs. AIDL
AIDL (Android Interface Definition Language) is basically an IPC mechanism that does the job of decomposing objects into primitives that the operating system can understand and then passing them across processes to perform IPC. Messengers are actually based on AIDL behind the scenes. When using Messenger, it creates a queue of all the client requests that the Service receives one at a time. All this happens on a single thread. In case you want your Service to handle multiple requests simaltaneously then you’ll need to make use of AIDL directly and make sure your Service is capable of multi-threading and also ensure thread-safety.
It is generally not recommended to use AIDL directly as it is a really complicated implementation and you need to ensure thread safety and handle multi threading.
Conclusion
We learnt how easy it is to achieve IPC using a Messenger with Services in Android. In the next article we’ll go through AIDL that you should use directly only if you’re certain that you must use it.
Hey Rishabh, is it possible to implement a network activity (like socket communication) and sending the progress of each step using Message objects as theService runs on UI thread and NetworkOnMainThreadException would arise if we perform any network activities,
You see, I have tried extending AsyncTask class to perform all my tasks but I was facing two problems:
1) I cannot update progress to the UI elements of MainActivity from this service. Btw. the service is called from the activity’s onStart() method.
2) The communication from Service —> Activity throws a NullPointerException when it is run on any other thread rather than UI Thread.
Can you help me solve this twisted problem?
Have you tried a LocalBroadcastManager to send updates back to the UI ? Here’s an SO link if you need help – http://stackoverflow.com/a/14695943
Hi, Rishabh!
Thanks for the article. I have a question. I’m working on an app that is basicly a timer (updates the UI each second) and a media player.
As of now it’s only activity based, but I’d like to use a service in order to support widget, notification bar icon and in general be able to work in background.
I have been looking at a bound service using Otto for events and now at AIDL and Messenger, but I’m confused as to which approach will work best. Do you have any suggestions? I basically want the whole “app” in the service, and the activity should only serve as UI and input.
Thank you!
-Fred
Hi Rishabh,
Many thanks for sharing this easy to follow tutorial.
I just wanted to tell you that I got confused when trying to apply your ideas in a scenario where a component needs to communicate multiple times with the service. In the aforementioned scenario it would be useful to first bind the component with the service, as you are doing in your onCreate() implementation, then upon a user event occurs send the message to the service (not as part of onServiceConnected() method) and onDestroy() unbind with the service.
Instead of TestHandler() it should be IncomingHandler() in:
Messenger mMessenger = new Messenger(new TestHandler());
Thanks a lot though for the article.
Looks like it’s a typo:
>new TestHandler()
`new IncomingHandler()` should be there.
Can you please put a full sample on Github?
For some reason, I’ve failed to make such an app that uses Messenger…
i think
Messenger mMessenger = new Messenger(new TestHandler());
should be
Messenger mMessenger = new Messenger(new IncomingHandler());