Two-way communication between processes through Messenger and Service in Android

Source: Internet
Author: User

Two-way communication between processes through Messenger and Service in Android

The Service and its caller in Android can be in the same App or in different apps. If the Service is in App1 and the client that calls the Service is in App2, we can use the Service to implement inter-process communication. This article describes how to use bindService and Messenger to implement inter-process communication (IPC). If you are not familiar with bindService binding and Binder, see bindService usage and Service lifecycle in Android. understanding this blog is the basis of this article.

The key to enabling the Service to communicate with other processes is Messenger, as well as the associated IBinder and Hanlder. If you are not familiar with Handler, see use of Handler in Android.

How to Use Messenger

Follow these steps to use Messenger:
1. The Service needs to implement a Hanlder to process messages received from the client.
2. Use the Handler to create a Messenger object, which will be referenced inside the Messenger object.
3. Use the created Messenger object to obtain an IBinder instance and return the IBinder to each client through the onBind method of the Service.
4. The client instantiates a Messenger object through the received IBinder object, which internally points to the Handler in the Service. The client can send messages to the Hanlder in the Service through this Messenger object.
5. After the Hanlder in the Service receives the message, it processes the message in the handleMessage method in the Handler.
6. Through steps 4th and 5th above, the client completes the one-way communication process of sending a message to the Service and the Service receives the message, that is, the client-> Service. If you want to implement the communication process from the Service to the client, that is, the Service-> client, the premise is that the client also needs to maintain a Messenger pointing to the Handler like the Service. When the client sends a Message to the Service in Step 4, set the Message replyTo attribute to the client's own Messenger. In this way, when the Service in step 1 processes the received Message in Handler's handleMessage, it can send the Message to the client through the Message Messenger, in this way, the Handler object maintained in the client will receive the Message from the Service, thus completing the communication process in which the Service sends a Message to the client and the client receives the Message.

In conclusion, two-way communication between the client and the Service can be completed in the last six steps:
Client> Service> Client

To demonstrate cross-process communication between the client and the Service, I have created two applications, ClientApp and ServiceApp. the names of the two applications are also known:
1. ClientApp, which is a client used to call services
2. ServiceApp, which is a Service end and used for client ClientApp calls

Service Code

There is only one MyService class in ServiceApp, and there are no other activities, that is, ServiceApp does not have any UI. The manifest. xml file of ServiceApp is as follows:


  
              
               
                                    
                 
     
            
       
  

We registered MyService in the manifest. xml file of ServiceApp and declared it as called by other apps through exported = "true. It should be noted that we set its action to custom action (com. ispring2.action. MYSERVICE) to facilitate the client to start MyService through its action. In addition, we set the value to android. intent. category. DEFAULT's category.

The MyService code in ServiceApp is as follows:

Package com. ispring2.serviceapp; 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. util. log; public class MyService extends Service {private static final int RECEIVE_MESSAGE_CODE = 0x0001; private static final int S END_MESSAGE_CODE = 0x0002; // clientMessenger indicates the client's Messenger, which can be obtained through the replyTo attribute of the client's Message, // It points internally to the client's ClientHandler instance, you can use clientMessenger to send a message to the Client private Messenger clientMessenger = null; // serviceMessenger is the Service's own Messenger, which internally points to the ServiceHandler instance // The client can build a Service-Side Messenger through IBinder, to send messages to the Service, // and receive and process messages from the client by ServiceHandler private Messenger serviceMessenger = new Messenger (new Servi CeHandler (); // MyService receives and processes messages from the client using ServiceHandler private class ServiceHandler extends Handler {@ Override public void handleMessage (Message msg) {Log. I (DemoLog, ServiceHandler-> handleMessage); if (msg. what = RECEIVE_MESSAGE_CODE) {Bundle data = msg. getData (); if (data! = Null) {String str = data. getString (msg); Log. I (DemoLog, MyService receives the following Message from the client: + str);} // get the client's own Messenger through Message replyTo, // The Service can send the clientMessenger = msg message to the client. replyTo; if (clientMessenger! = Null) {Log. I (DemoLog, MyService replies to the client); Message msgToClient = Message. obtain (); msgToClient. what = SEND_MESSAGE_CODE; // you can use Bundle to send cross-Process Information Bundle bundle = new Bundle (); bundle. putString (msg, hello, client, I am MyService); msgToClient. setData (bundle); try {clientMessenger. send (msgToClient);} catch (RemoteException e) {e. printStackTrace (); Log. e (DemoLog, MyService failed to send information to the client: + e. getMessage () ;}}}}@ Override public void onCreate () {Log. I (DemoLog, MyService-> onCreate); super. onCreate () ;}@ Override public IBinder onBind (Intent intent) {Log. I (DemoLog, MyServivce-> onBind); // obtain the IBinder corresponding to the Service's own Messenger, and send it to all clients for sharing return servicemesinder. getBinder () ;}@ Override public void onDestroy () {Log. I (DemoLog, MyService-> onDestroy); clientMessenger = null; super. onDestroy ();}}
MyService has an internal class ServiceHandler that inherits from Hanlder and overwrites the handleMessage method. MyService uses ServiceHandler to receive and process messages from the client. In MyService, serviceMessenger is initialized using ServiceHandler's instance. Servicemesler is the Service's own Messenger, which internally points to the ServiceHandler instance. The client can build the Service-Side Messenger through IBinder to send messages to the Service, serviceHandler receives and processes messages from the client. Client code

The client application ClientApp is a MainActivity, and there are only two buttons on the Interface: bindService and unbindService. The interface is as follows:

The Code is as follows:

Package com. ispring. clientapp; import android. app. activity; import android. content. componentName; import android. content. intent; import android. content. serviceConnection; import android. content. pm. packageManager; import android. content. pm. resolveInfo; import android. OS. bundle; import android. OS. handler; import android. OS. IBinder; import android. OS. message; import android. OS. messenger; import android. OS. remoteE Xception; import android. util. log; import android. view. view; import android. widget. button; public class MainActivity extends Activity implements Button. onClickListener {private static final int SEND_MESSAGE_CODE = 0x0001; private static final int RECEIVE_MESSAGE_CODE = 0x0002; private boolean isBound = false; // The action private final String SERVICE_ACTION = com. ispring2.action. MY SERVICE; // servicemesler indicates the Service-Side Messenger, which points internally to the MyService ServiceHandler instance // you can use serviceMessenger to send a message private Messenger serviceMessenger = null to MyService; // clientMessenger is the client's own Messenger, which internally points to the ClientHandler instance // MyService can get clientMessenger through Message replyTo, so that MyService can send messages to the client, // The client receives and processes the messages from the Service. private Messenger clientMessenger = new Messenger (new ClientHandler (); // The Client The user uses ClientHandler to receive and process messages from the Service. private class ClientHandler extends Handler {@ Override public void handleMessage (Message msg) {Log. I (DemoLog, ClientHandler-> handleMessage); if (msg. what = RECEIVE_MESSAGE_CODE) {Bundle data = msg. getData (); if (data! = Null) {String str = data. getString (msg); Log. I (DemoLog, the client receives the Service Message: + str) ;}}} private ServiceConnection conn = new ServiceConnection () {@ Override public void onServiceConnected (ComponentName, IBinder binder) {// establish a connection Log between the client and the Service. I (DemoLog, client onServiceConnected); // We can initialize a Messenger serviceMessenger = new Messenger (binder); isBound = True; Message msg = Message. obtain (); msg. what = SEND_MESSAGE_CODE; // Message cannot be used for cross-process Message communication. if obj is set to non-Parcelable, use Bundle // msg. obj = Hello, MyService, I am a client; Bundle data = new Bundle (); data. putString (msg, hello, MyService, I am a client); msg. setData (data); // you need to set the Message replyTo to clientMessenger of the client, // so that the Service can send the Message msg to the client through it. replyTo = clientMessenger; try {Log. I (DemoLog, the client sends information to the service); serviceMessenge R. send (msg);} catch (RemoteException e) {e. printStackTrace (); Log. I (DemoLog, the client fails to send a message to the service: + e. getMessage () ;}@ Override public void onServiceDisconnected (ComponentName name) {// the client and Service lose connection serviceMessenger = null; isBound = false; Log. I (DemoLog, client onServiceDisconnected) ;};@ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setCon TentView (R. layout. activity_main) ;}@ Override public void onClick (View v) {if (v. getId () = R. id. btnBindService) {// click the bindService button if (! IsBound) {Intent intent = new Intent (); intent. setAction (SERVICE_ACTION); intent. addCategory (Intent. CATEGORY_DEFAULT); PackageManager pm = getPackageManager (); // you can use an implicit Intent to obtain information about the Service that may be started. ResolveInfo = pm. resolveService (intent, 0); if (info! = Null) {// If ResolveInfo is not empty, we can find the corresponding Service through the implicit Intent above // we can get the package information of the Service to be started and the type String packageName = info. serviceInfo. packageName; String serviceNmae = info. serviceInfo. name; // then we need to construct a ComponentName Based on the package name and Class name of the Service to set the specific component information that intent wants to start, in this way, the intent is converted from an explicit intent implicitly. The reason why the great charge is converted from an implicit intent to an explicit intent is that it starts from Android 5.0 Lollipop, // Android no longer supports starting a Service by using an implicit intent, service can only be started in explicit intent mode. // In versions earlier than Android 5.0 Lollipop, you can use implicit intent to start Service ComponentName componentName = new ComponentName (packageName, serviceNmae); intent. setComponent (componentName); try {Log. I (DemoLog, the client calls the bindService method); bindService (intent, conn, BIND_AUTO_CREATE);} catch (Exception e) {e. printStackTrace (); Log. e (DemoLog, e. getMessage () ;}}} else if (v. getId () = R. id. btnUnbindService) {// click the unbindService button if (isBound) {Log. I (DemoLog, the client calls the unbindService method); unbindService (conn );}}}}
ClientHandler inherits from Hanlder and overwrites the handleMessage method. The client uses ClientHandler to receive and process messages from the Service. We initialized clientMessenger with the ClientHandler instance. ClientMessenger is the client's own Messenger, which points internally to the ClientHandler instance. MyService can get clientMessenger through Message replyTo, so that MyService can send messages to the client, the ClientHandler receives and processes messages from the Service. Analysis result

The code output statement is added to each key node of the above Code. We observe the output result through DDMS.

First open the ClientApp and click the bindService button above. The output result in DDMS is as follows:

We can see from the figure above that the PID of the client ClientApp and ServiceApp processes is 2524 and 2542, respectively, and they run in different processes.

We analyze the code execution process through the output results:
1. First, we need to understand that Messenger is bound with Handler and IBinder. Therefore, the constructor of Messenger has two types:
A. one is to pass in a Hanlder, create a Messenger Based on the passed Handler, And the Messenger points to the Handler. When we send information to the Messenger, the Handler will receive information and process the message, this constructor usually constructs the Messenger of this class in a class, for example, in MyService, serviceMessenger is initialized with ServiceHandler instances and clientMessenger is initialized with ClientHandler instances on the client. This kind of Messenger can be seen as a local Messenger. After creating the Messenger, you can use the getBinder () method to obtain the corresponding IBinder instance.
B. The other is to input an IBinder and create a remote Messenger Based on the imported IBinder instance. This kind of constructor is usually in the client, get the IBinder returned by the onBind method of the Service, and then initialize a remote Messenger based on this IBinder. This Messenger points to a Service rather than a client. Therefore, this Messenger is a remote Messenger. For example, serviceMessenger in the client is a remote Messenger that points to MyService.

2. after clicking the bindService button in the client, we start MyService through intent, MyService starts to execute its lifecycle, first executes the onCreate callback method, and then executes the onBind callback method, when the onBind method is executed, the method returns the binder corresponding to the local serviceMessenger in MyService and returns it to the client.

3. After the onBind method of MyService is returned, IBinder will be passed into the onserviceconnection object onServiceConnected callback method of the client. Execution of this method indicates that the client has established a connection with MyService. In this case, a serviceMessenger pointing to MyService is initialized Based on the IBinder of MyService. serviceMessenger is a remote Messenger.

4. After obtaining serviceMessenger pointing to MyService, we can send a message to MyService through it. We have constructed a Message and set data for it through Bundle. Note that we also set the Message replyTo as the client's clientMessenger, so that the Service can send messages to the client. Then, the Message is sent to MyService through the Code serviceMessenger. send (msg.

5. After the client sends a message to MyService through servicemesler, ServiceHandler of MyService receives the message and processes the message in handleMessage. We first read the Bundle data of the Message and print it out. Then we get the Messenger pointing to the client through the Message replyTo, and save it in the clientMessenger of MyService. clientMessenger is a remote Messenger compared with MyService. Then we construct a Message, and set data through Bundle, and send a Message back to the client by executing the code clientMessenger. send (msgToClient. Because we save clientMessenger, we can actively send messages to the client at any time in the subsequent process.

6. after MyService sends a Message to the client through clientMessenger, the client's ClientHandler receives the Message and processes the Message in its handleMessage method: reads the Bundle data of the Message from MyService and prints it out.

Through the preceding steps, we can achieve cross-process two-way communication between customer tickets and services:
1. The client sends a message to the Service, and the Service reads the information, that is, the client-> Service
2. The Service replies to the client, and the client reads the information, that is, the Service-> Client

The above is the code execution process when we click the bindService button on the client. When we click the unbindService button, the output result of DDMS is as follows:

When unbindService is executed, the client is disconnected from MyService. No other client is connected to MyService. Therefore, MyService executes the onUnbind callback method and then implements the onDestroy callback method, myService destruction.

Notes

Note the following two points in the client code:
1. when you run the bindService (intent, conn, BIND_AUTO_CREATE) code, if intent only sets action and category and does not explicitly specify the component to be started, the intent is implicit. In Android 5.0 and later versions, you must use an explicit intent to start the service. If you use an implicit intent, the following error is returned:

3072-3072/com. ispring. clientapp E/DemoLog: Service Intent must be explicit: Intent {act = com. ispring2.action. MYSERVICE cat = [android. intent. category. DEFAULT]}

The solution is to first build an implicit Intent, and then obtain information about services that may be started through PackageManager's resolveService. If ResolveInfo is not empty, we can find the corresponding Service through the implicit Intent above, and we can also obtain the package information and type of the Service to be started. Then, we need to construct a ComponentName Based on the package name and Class Name of the Service to set the specific component information of the intent to be started, in this way, intent is converted from implicit to an explicit intent. Then we can pass the explicit intent to the bindService method to start the service.
For details, see connection: connections.

2. when Messenger is used to transmit messages between two processes, the obj of the Message cannot be set to a non-Parcelable object. For example, in the case of cross-process, if the obj of Message is set to a String object, the following error is reported when the send (Message) method is executed by Messenger:
Does html not support style? No matter whether style is set. If you know it, please let us know.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.