• [free] exclusive: this is a very valuable collection of Android knowledge system!!

type describe when
Topic selection silencezwm 0.5 hours
Time to write October 25, 2017 5.5 hours
reviewing Silencezwm, Mya Tingting 2 hours
Check online silencezwm 1 hour

Tips: Four links, a total of about 9 hours of careful polishing to complete the online, at the same time, thank you very much for participating in the review students.


>>> [piglet’s legendary life] :

The cartoon depicts a little pig born, living in a pigsty and growing up happily. One day, when the pigs are old enough, the slaughterhouse owner will ship in fat pigs for slaughter and then sell the meat around the world.

After reading the cartoon, don’t you think little pig’s life is a little sad, blame it on the hateful human, no meat is not happy, ha ha.

Behind the wonderful cartoon, there is always a bit of technical knowledge hidden. This article will introduce you to the “Android cross-process communication” related knowledge, through this study, you can learn:

The difference between single-process communication and multi-process communication

Two, cross – process communication common five ways to achieve

3. Precautions for cross-process communication

The difference between single-process communication and multi-process communication

Popular concept: Inter-process Communication (IPC) mechanism, namely inter-process Communication or cross-process Communication mechanism, refers to the Process of data exchange between two processes.

1.1. Single-process communication

Long ago, piglets lived in pigsty from birth to death, with no invasion from outsiders. Similarly, in Android development, by default, applications run in a main process created with the project package name (like a pigsty), for example

The project package name is com.silenceZwm.ipcdemo. The default process name is com.silencezwm.ipcdemoCopy the code

Single-process communication is like this: piglets of different types (different components in Android) live (run) in the same pigsty (in the same process).

1.2. Multi-process communication

Suddenly one day, there came a merchant on the right side of the river. He saw some fat pigs wandering on the other side. So he saw an opportunity to make a fortune and built a slaughterhouse here. Then often through the boat to the other side of the fat pig transport, slaughter, full of money.

This is similar to an Android application, where there is only one process running, but because the product has a strange requirement, we have to open an extra process to fulfill the requirement.

After some grumbling, the programmers finally got to work.

They add the Android: Process attribute to the corresponding component in the Androidmanifest.xml file and specify the process name. Then both activities are started, but the BActivity is specified to run in the new process. When the program runs, you can see that there are two processes running, as shown in the figure below:

We know that when communicating in a single process, components can communicate freely with each other because they are all in the same memory space. How do multiple processes communicate with each other?

In the cartoon, the slaughterhouse owner uses a boat to transport fat pigs from the other side of the river. Because of the boat, the owner can cross the river to the other side. Therefore, in Android cross-process communication, we also need a ship with the same function, which is Binder, through which the process can smoothly exchange data.

Two, cross – process communication common five ways to achieve

The five common implementations fall into two broad categories: four-component cross-process communication and AIDL.

2.1. Four components

The Activity, Service, BroadcastReceiver, and Content Provider components only need to add the Android: Process attribute to the corresponding component of the androidmanifest.xml file and specify the process name. After the program runs, they will run in different processes, the communication between them, the official has given us a very good encapsulation, so it is very convenient to use, there is no more explanation here.

2.2, AIDL

Popular concept :AIDL (Android Interface Definition Language) is the Android interface definition Language.

In order to apply AIDL technology, at least two processes need to exist. Process A communicates with process B through the defined AIDL interface file. The specific implementation steps are as follows:

1. Prepare two processes: create a new project IPCDemo, and then create a new Module IPCClient. In this way, we have prepared two processes.

Create AIDL file: Create an AIDL interface file in IPCDemo on the server side: imyaidlInterface. AIDL, which will implement basicTypes method by default, and then define a login method, IMyAidlInterface.

package com.silencezwm.ipcdemo; Void basicTypes(int anInt, long aLong,) {/** * int IMyAidlInterface {/** * int basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString); */ void login(String username, String password); */ void login(String username, String password); }Copy the code

3, Build project: Android Studio will automatically generate a Java file from IInterface, IMyAidlInterface.

package com.silencezwm.ipcdemo; Public interface IMyAidlInterface extends Android.os. IInterface {/** * local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.silencezwm.ipcdemo.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.silencezwm.ipcdemo.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.silencezwm.ipcdemo.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.silencezwm.ipcdemo.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.silencezwm.ipcdemo.IMyAidlInterface))) { return ((com.silencezwm.ipcdemo.IMyAidlInterface) iin); } return new com.silencezwm.ipcdemo.IMyAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 ! = data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_login: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _arg1; _arg1 = data.readString(); this.login(_arg0, _arg1); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.silencezwm.ipcdemo.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; AIDL} / * * * * / @ Override the default implementation method public void basicTypes (int anInt, long aLong, Boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }} /** * defines a login method, */ @override public void login(java.lang.String username, java.lang.String password) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(username); _data.writeString(password); mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void basicTypes(int anInt, long aLong, Boolean aBoolean, float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float java.lang.String aString) throws android.os.RemoteException; /** * defines a login method, Public void login(java.lang.String username, java.lang.String password) throws android.os.RemoteException; }Copy the code

Create a new Service (SRC /main/ Java/package name) and register it in the configuration file. Then set the action value to com.silencezwm.ipcdemo. IMyAidlInterface.Stub; aidlService.java; IMyAidlInterface.

package com.silencezwm.ipcdemo; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; public class AidlService extends Service { private static final String TAG = AidlService.class.getName(); public AidlService() { } @Override public IBinder onBind(Intent intent) { return new MyBinder(); } class MyBinder extends IMyAidlInterface.Stub { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { Log.d(TAG, "=====:basicTypes");  } @Override public void login(String username, String password) throws RemoteException { Log.d(TAG, "=====:login" + username + "==" + password); }}}Copy the code

5, copy the AIDL directory file: so far, the server side of the code to complete, next we just need to complete the client side of the call code. Quite simply, first copy the entire server aiDL folder into the client SRC /main directory (for reasons explained later), and then build the project. In this case, both the client and the server have the same code as AIDL.

6, bind to Service: Bind the package name of the Service and the action in the client where you want the Service to be. Then connect the Service to the IBinder object that is returned. Through IMyAidlInterface. Stub. Converting asInterface method we define aidl object, then call the method that we defined according to the object can complete the whole process of communication, the client MainActivity. Call the Java code is as follows:

package com.silencezwm.ipcclient; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import com.silencezwm.ipcdemo.IMyAidlInterface; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button mBtnLogin; private IMyAidlInterface mIMyAidlInterface; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnLogin = (Button) findViewById(R.id.btn_login); mBtnLogin.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn_login: Intent intent = new Intent(); Intent.setaction ("com.silencezwm.ipcdemo"); Intent.setpackage ("com.silencezwm.ipcdemo"); intent.setPackage("com.silencezwm.ipcdemo") bindService(intent, new ConnectCallBack(), Context.BIND_AUTO_CREATE); break; }} class ConnectCallBack implements ServiceConnection{@override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder); login(); } @override public void onServiceDisconnected(ComponentName ComponentName) {mIMyAidlInterface = null; } } private void login() { try { mIMyAidlInterface.login("silencezwm", "123456"); } catch (RemoteException e) { e.printStackTrace(); }}}Copy the code

7, result verification: harvest season is coming, first run the server program, then run the client program, click the login button, if there is no accident, we have bound the server Service, and call the login method, the data transfer to the server, let’s look at the Log print information:

Looking back at the AIDL implementation process, it’s not that complicated. At this point, intelligent people often have a question:

How does the client data get to the server?Copy the code

Let’s take a look. The following code mainly involves the automatically generated imyaidlInterface.java file.

Calling code on the client, we know that once the binding after the success of the Service, returns an IBinder object, call IMyAidlInterface. The Stub. AsInterface (IBinder) method to convert the object in order to we defined an AIDL interface objects, What does this method do? Take a look at:

private static final java.lang.String DESCRIPTOR = "com.silencezwm.ipcdemo.IMyAidlInterface"; public Stub() { this.attachInterface(this, DESCRIPTOR); } public static com.silencezwm.ipcdemo.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.silencezwm.ipcdemo.IMyAidlInterface))) { return ((com.silencezwm.ipcdemo.IMyAidlInterface) iin); } return new com.silencezwm.ipcdemo.IMyAidlInterface.Stub.Proxy(obj); }Copy the code

In this code, the Stub constructor is called first, followed by the attachInterface method:

private IInterface mOwner; private String mDescriptor; . public void attachInterface(IInterface owner, String descriptor) { mOwner = owner; mDescriptor = descriptor; }Copy the code

Fairly well understood, assignment of interface objects and string identifiers. Then in the asInterface method, the object is checked locally in the IBinder according to the identifier, so call the obj.queryLocalInterface(DESCRIPTOR) method, and continue with the source binder.java

public IInterface queryLocalInterface(String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}
Copy the code

This means that if there is a local IInterface object for this identifier, the mOwner object initialized in the constructor is returned directly, otherwise null is returned, because we are dealing with cross-process communication. As the code continues, it is obvious that the following code will call:

return new com.silencezwm.ipcdemo.IMyAidlInterface.Stub.Proxy(obj);
Copy the code

The code literal returns the IBinder code object as follows:

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
    mRemote = remote;
}
Copy the code

At this point, we have a proxy object for IBinder, from which we can call the login method we defined earlier:

@Override public void login(java.lang.String username, java.lang.String password) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(username); _data.writeString(password); mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }}Copy the code

Here comes an important class called Parcel, which inherently has the ability to transfer data across processes. In the cartoon at the beginning of the article, instead of just taking the pigs to the boat, in case the pigs run away and fall into the river, the slaughterhouse owner prepares some cages for pigs. First, drive the piglets into the pig cage, wait for the boat to dock, open the pig cage, the piglets can be released. A Parcel can be called a data transport carrier because we write the data that needs to be delivered into the Parcel and then, when it reaches the target process, read the data from the Parcel. Parcel supports a wide range of data types for our daily development needs.

Now do you know how data from the client is passed to the server?

3. Precautions for cross-process communication

3.1 AiDL files and package names of the client and server must be consistent; otherwise, they cannot communicate normally.

Intent.setpackage (“com.silencezwm.ipcdemo”); intent.setPackage(“com.silencezwm.ipcdemo”); intent.setPackage(“com.silencezwm.ipcdemo”) You’ll run into this error Java. Lang. IllegalArgumentException: Service Intent must be explicit:.

3.3, passing entity classes across processes must be serialized, try it.

3.4 The amount of memory that a Parcel takes up varies depending on the amount of data you send.

Well, that’s the end of this article about “Android Cross-process Communication”, thank you for coming!


  • [free] exclusive: this is a very valuable collection of Android knowledge system!!