Summary of inter-process communication (IPC) methods in Android

Summary of inter-process communication (IPC) methods in Android

Summary of inter-process communication (IPC) methods in Android

Source: Geek Headlines

      IPC is inter-process communication or cross-process communication, which refers to the process of inter-process communication between two processes. A process on PCs and mobile devices refers to a program or an application, so we can simply understand inter-process communication as communication between different applications. Of course, this statement is not rigorous.

In Android, an independent virtual machine is assigned to each application, or each process is assigned an independent virtual machine. Different virtual machines have different address spaces in memory allocation, which results in different Virtual machines need to resort to other means to access data each other. The following respectively introduces the way to implement IPC in Android.

1. Use Bundle

We know that the three major components (Activity, Service, Receiver) in Android support the transfer of Bundle data in Intent. Because Bundle implements the Parcelable interface, it can easily transfer between different processes. When we start the Activity, Service, and Receiver of another process in one process, we can attach the information we need to transmit to the remote process in the Bundle and send it out through the Intent. Note here: The data we transmit must be able to be serialized.

Let's take a look at an example of using Bundle for inter-process communication:

Intent intent = new Intent(MainActivity.this, TwoActivity.class); Bundle bundle = new Bundle(); bundle.putString("data", "test data"); intent.putExtras(bundle); startActivity(intent); Copy code

It is very easy to use Bundle for inter-process communication. You should also notice that this way of inter-process communication can only be a simple one-way data transmission, and its use has certain limitations.

2. Use file sharing

Sharing files is also a good inter-process communication method in the future. Two processes exchange data by reading/writing the same file. For example, process A writes data to file FILE, process B can obtain this data by reading this file. In this way, in addition to exchanging simple text messages, we can also serialize an object to the file system, and another process can restore the object through deserialization.

For example, create a thread in the A process to write data:

new Thread(new Runnable() { @Override public void run() { User user = new User(1, "user", false); File cachedFile = new File(CACHE_FILE_PATH); ObjectOutputStream objectOutputStream = null; try{ objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); }catch(IOException e){ e.printStackTrace(); }finally{ objectOutputStream.close(); } } }).start(); Copy code

Create a thread in the B process to read data:

new Thread(new Runnable() { @Override public void run() { User user = null; File cachedFile = new File(CACHE_FILE_PATH); if(cachedFile.exists()){ ObjectInputStream objectInputStream = null; try{ objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile)); user = objectInputStream.readObject(user); }catch(IOException e){ e.printStackTrace(); }finally{ objectInputStream.close(); } } try{ objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); }catch(IOException e){ e.printStackTrace(); }finally{ objectOutputStream.close(); } } }).start(); Copy code

Sharing data in this way of file sharing has some requirements for the format of the file. For example, it can be a text file or an XML file, as long as the read and write parties agree on the data format. Although this method of inter-process communication is convenient, it also has limitations, such as concurrent read/write, which can lead to more serious problems, such as incomplete data read or the data read is not up to date. Therefore, file sharing is suitable for communication between processes that do not require high data synchronization, and concurrent read/write issues must be properly handled.

3. Use Messenger

We can also communicate between processes through Messenger. Put the data we need to transmit in Messenger, and then we can easily realize the data transmission between processes. Messenger is a lightweight IPC solution. Its underlying implementation is AIDL. We will introduce AIDL in the face-to-face meeting.

The use of Messenger is also relatively simple. There are several steps to implement a Messenger, which is divided into server and client:

Server process: Create a Service in process A to handle connection requests from other processes, and at the same time create a Handler and use it to create a Messenger object, and then return the Binder at the bottom of the large Messneger object in the onBind of the Service.

public class MessengerService extends Service{       private Handler MessengerHandler = new Handler(){              @Override           public void handleMessage(Message msg) {              //Message processing...                 };       //Create a server-side Messenger       private final Messenger mMessenger = new Messenger(MessengerHandler);       @Override       public IBinder onBind(Intent intent) {            //Return the Ibinder object to the client, and the client uses the object to access the server           return mMessenger.getBinder();       }       @Override       public void onCreate() {           super.onCreate();       }   } Copy code

Client process: First bind the Service of the remote process in process B. After the binding is successful, create a Messenger object based on the IBinder object returned by the Service, and use this object to send messages. In order to receive the message returned by the Service, the client I also created my own Messenger and sent it to the Service end, and the Service end can send messages to the client through the client's Messenger. The specific code is as follows:

public class MessengerActivity extends Activity{       private ServiceConnection conn = new ServiceConnection(){           @Override           public void onServiceConnected(ComponentName name, IBinder service) {               //Create Messenger based on the obtained IBinder object               mService = new Messenger(service);               //Communication can be carried out through the obtained mService         }          };              //In order to receive a reply from the Service, the client needs to create a Messenger and Handler to receive the message       private Handler MessengerHander = new Handler(){           @Override           public void handleMessage(Message msg) {   //Message processing                     }       };       private Messenger mGetMessenger = new Messenger(MessengerHander);       @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.activity_messenger);           init();       }       private void init() {           intent = new Intent(MessengerActivity.this, MessengerService.class);          indService(intent, conn, Context.BIND_AUTO_CREATE);                             }           });       }       @Override       protected void onDestroy(){           unbindService(conn);           super.onDestroy();       }   }  Copy code

Here is a diagram of the working principle of Messenger to better understand Messenger:

\

The internal message processing of Messenger is implemented using Handler, so it processes the messages sent by the client in a serial manner. If a large number of messages are sent to the server, the server can only process one by one. If the amount of concurrency is large, use Messenger is not suitable, and the main role of Messenger is to deliver messages. Many times we need to call server-side methods across processes. This requirement Messenger cannot do.

4. Use AIDL:

AIDL (Android Interface Definition Language) is an IDL language used to generate code for inter-process communication (IPC) between two processes on an Android device. If you want to call an operation of an object in another process (for example, Service) in one process (for example, Activity), you can use AIDL to generate serializable parameters.

AIDL is a lightweight implementation of IPC, using syntax that is familiar to Java developers. Android also provides a tool that can automatically create a Stub (class framework, class skeleton). When we need to communicate between applications, we need to follow the following steps:
1. Define an AIDL interface
2. Implement the corresponding Stub for the remote service (Service)
3. "Expose" the service to the client program for use

There is an introduction to AIDL in the official document: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

The first sentence is the most important, "Only when you allow different clients to access your service and need to deal with multi-threading issues, you must use AIDL", in other cases you can choose other methods, such as using Messager, you can also Cross-process communication. It can be seen that AIDL handles multi-threaded, multi-client concurrent access. The Messager is single-threaded processing.

The great advantage of AIDL is that we can directly call the methods exposed by the server process. Here is a brief introduction to the use of AIDL:

Server

The server must first create a Service to listen to the client's request, and then create an AIDL file, declare the interface exposed to the client in the AIDL file, and finally implement the AIDL interface in the Service.

(1) Create aidl interface file

AIDL uses a simple syntax to declare an interface, describe its methods and method parameters and return values. These parameters and return values can be of any type, even other AIDL-generated interfaces. The important thing is that all non-built-in types must be imported, even if these types are in the same package as the interface.

package com.example.android; interface IRemoteService {     int getPid();     void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,             double aDouble, String aString); } Copy code

(2) Expose the interface to the client:

public class DDService extends Service {     @Override     public void onCreate() {         super.onCreate();         System.out.println("DDService onCreate........" + "Thread: "+ Thread.currentThread().getName());     }     @Override     public IBinder onBind(Intent arg0) {         System.out.println("DDService onBind");         return mBinder;     }       private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {         public int getPid(){             System.out.println("Thread: "+ Thread.currentThread().getName());             System.out.println("DDService getPid ");             return Process.myPid();         }         public void basicTypes(int anInt, long aLong, boolean aBoolean,             float aFloat, double aDouble, String aString) {             System.out.println("Thread: "+ Thread.currentThread().getName());             System.out.println("basicTypes aDouble: "+ aDouble +" anInt:" + anInt+" aBoolean "+ aBoolean+" aString" + aString);         }     }; } Copy code

In this way, our server is complete. Run the server on the simulator (or on the mobile phone). After a while, you can look at the printed information, focusing on the "thread name".

Client

What the client does is much simpler. 1. you need to bind the server service. After the binding is successful, the Binder object returned by the server is converted to the type of the AIDL interface, and then all methods in the AIDL can be called.

public class MainActivity extends Activity {     private IRemoteService remoteService;     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);     }           ServiceConnection conn = new ServiceConnection() {                   @Override         public void onServiceDisconnected(ComponentName name) {         }                   @Override         public void onServiceConnected(ComponentName name, IBinder service) {             remoteService = IRemoteService.Stub.asInterface(service);             try {                 int pid = remoteService.getPid();                 int currentPid = Process.myPid();                 System.out.println("currentPID: "+ currentPid +" remotePID:" + pid);                 remoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "Our love, I understand");             } catch (RemoteException e) {                 e.printStackTrace();             }             System.out.println("bind success! "+ remoteService.toString());         }     };               /**      * Monitor button click      * @param view      */     public void buttonClick(View view) {         System.out.println("begin bindService");         Intent intent = new Intent("duanqing.test.aidl");         bindService(intent, conn, Context.BIND_AUTO_CREATE);     }       @Override     protected void onDestroy() {         super.onDestroy();         unbindService(conn);     } } Copy code

In this way, the inter-process communication through AIDL is realized, is it very simple, but this seems simple, in fact, the underlying Android has done a lot for us, the core is Binder, interested readers can learn about the principle of Binder.

5. Use ContentProvider:

ContentProvider (content provider) is one of the four major components in Android . In order to exchange data between applications, Android provides ContentProvider. ContentProvider is an API for data exchange between different applications. Once an application passes ContentProvider Exposing its own data operation interface, no matter whether the application is started or not, other applications can manipulate the data in the interface through the interface, including adding, deleting, modifying, and checking. ContentProvider is divided into system and custom, and the system is data such as contacts and pictures.

The steps to develop a ContentProvider are simple:

Define your own ContentProvider class, which inherits the ContentProvider base class;

Registering this ContentProvider in AndroidManifest.xml is similar to Activity registration. When registering, you must bind a domain name to the ContentProvider;

When we register the ContentProvider, other applications can access the data exposed by the ContentProvider.

ContentProvider only exposes data that can be manipulated by other applications, and other applications need to manipulate the data exposed by ContentProvider through ContentReslover. Context provides the getContentResolver() method to obtain the ContentResolver object. After obtaining it, you can add, delete, modify, and check the exposed data.

The steps to use ContentResolver to manipulate data are also very simple:

Call Activity's getContentResolver() to get the ContentResolver object

Operate the database according to the called ContentResolver's insert(), delete(), update(), and query() methods.

The code will not be posted. This is one of the four major components of Android. I believe readers are already familiar with it.

6. Use Broadcast

Broadcasting is a way of passive cross-process communication. When a program sends a broadcast to the system, other applications can only passively receive the broadcast data. This is like broadcasting on a radio station. The audience can only listen passively, but cannot actively communicate with the radio station.

BroadcasReceivert is essentially a system-level listener. He specializes in monitoring Broadcasts sent by various programs. Therefore, he has his own process. As long as there is a matching Intent to be broadcasted, BroadcasReceivert will always be activated. We know that only after registering for a broadcast, the broadcast receiver can receive the broadcast. One behavior of broadcast registration is to register the IntentFilter that you are interested in to the AMS (ActivityManagerService) of the Android system, which stores a list of IntentFilters. The broadcast sender sends its own IntentFilter action behavior to AMS, and then traverses the IntentFilter list in AMS to see who has subscribed to the broadcast, and then sends the message traversal to the Activity or Service that registered the corresponding IntentFilter ----- That is, the abstract method onReceive() method is called. Among them, AMS plays the role of an intermediate bridge.
The program to start BroadcasReceivert only needs two steps:

1) Create the Intent of BroadcasReceivert that needs to be started;

2) Call the sendBroadcast() or sendOrderBroadcast() method of Context to start the specified BroadcasReceivert;

Whenever a Broadcast event occurs, the system will create a corresponding BroadcastReceiver instance and automatically trigger the onReceiver() method. After the onReceiver() method is executed, the BroadcastReceiver instance will be destroyed.

Note: Try not to do time-consuming operations in the onReceiver() method. If the onReceiver() method cannot complete the event processing within 10 seconds, Android will think that the program is not responding, and the familiar ANR dialog box will pop up. If we need to perform some time-consuming operations after receiving the broadcast message, we can consider starting a Server through Intent to complete the operation. We should not start a new thread to complete the operation, because the BroadcastReceiver life cycle is very short, and the new thread may not be created yet. After the execution, the BroadcastReceiver has been destroyed, and if the BroadcastReceiver ends, although there are still new threads started to perform tasks in the process he is in, there are no components in the process, so the system will reclaim the memory when the memory is tight. Process, which causes the child thread started by BroadcastReceiver to be unable to complete execution.

7. Use Socket:

Socaket is also a way to realize inter-process communication. Socaket has also become a "socket", which is a concept in network communication. Through Socaket, we can easily carry out network communication. Network address book can be realized. Then cross-process communication can be realized. Isn t it the same? But Socaket is mainly used in network communication, and interested readers can find out for themselves.

I sincerely hope that the above summary will be helpful to everyone. I hope you can criticize and correct any problems, thank you!







\

Even if it s a small step,
I want to share with you

download now

\