Android interprocess communication

Why understand Android’s interprocess communication mechanism

Android developers can use the apis provided by the system if they don’t understand the interprocess communication mechanism, but if they want to reach a higher level, they can’t just call the API. Encountered some problems at work, or want to learn the source code of some functions, or want to improve the performance of the APP and so on, all these work we need to go to see the system source code, the system source of inter-process communication is everywhere, if you don’t understand the interprocess communication mechanism, it is hard to understand the system source code, and easy to get lost in a lot of code.

Android interprocess communication mechanism

Why use Binder as the Android interprocess communication mechanism

This article is well written. The main purpose is to make up for the performance and security deficiencies of other interprocess communication methods in Linux. Binder is not Google’s original Android technology. Binder came from OpenBinder, which predates Android. So if you want to instantly communicate between processes on Android, the key is to understand the Binder mechanism.

Binder Interprocess communication framework

Binder agents are used to communicate between two applications or processes. Binder agents are also used to communicate between two applications or processes. For example, ActivityManagerService(AMS) is a system service process. In addition, the application of WIFI, positioning and media services are all system processes, and the application must communicate with Binder to use these system services.

What exactly is a Binder

We’ve been talking about using a Binder mechanism for interprocess communication. What exactly is a Binder? Is it a Java class or an underlying driver? The Binder mechanism is commonly referred to as a set of cross-process communication functions made up of various Binder related code layers of the Android system. Binder code runs from the bottom driver layer to the top application layer, so understanding the Binder mechanism requires a patient layer by layer analysis.

Binder mechanism code structure

How to understand AIDL

From above, we don’t see any AIDL related information, that is to say is nothing to do with AIDL Binder mechanism, so if you want to in our daily across processes must write an AIDL class then generated by the AS some Java classes, we use these classes implement inter-process communication, the aim is actually made the AS help us to generate some template code, To reduce our effort and error probability, Binder communication can be implemented without AIDL and the Binder mechanism can be better understood. I will write a Demo process that has AIDL files and generates code that we don’t use, just for comparison. Binder communication is implemented with minimal code and aiDL-generated code is compared to aiDL-generated code to better understand aiDL-generated code. Code making

Manual inter-process communication without USING ADIL

The project structure

In the code, client is the client and server is the server

The client process sends a string to the server. The server process receives the string and displays it on the interface. The project did not use AIDL to generate Binder communication classes for us. Instead, we implemented Binder communication in the simplest way, so we could see the key parts of Binder communication. First, we need to know that objects of classes that implement the IBinder interface can be passed across processes.

The service side

1. RemoteService on the server inherits Service 2. Create a ServerBinder class that inherits Binder and overrides the onTransact method to handle Client calls. Binder implements IBinder interface 3. The server overrides the Service’s onBind method and returns a ServerBinder object that is ultimately passed to the Client.

public class RemoteService extends Service { public static final int TRANSAVTION_showMessage = IBinder.FIRST_CALL_TRANSACTION; @Nullable @Override public IBinder onBind(Intent intent) { return new ServerBinder(); } static class ServerBinder extends Binder { public ServerBinder() { } @Override protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { switch (code) { case TRANSAVTION_showMessage: String message = data.readString(); Log.d("ServerBinder", "showMessage " + message); if (ServerMainActivity.tvShowMessage ! New Handler(looper.getMainLooper ()).post(new Runnable() {@override public void run() {Override public void run() { ServerMainActivity.tvShowMessage.setText(message); }}); } if (reply ! = null) { reply.writeNoException(); } return true; } return super.onTransact(code, data, reply, flags); }}}Copy the code
The client

1. The client creates a ServiceConnection object to establish a connection with the server and obtain IBinder object 2 from the server. The client connects to RemoteService on the server through bindService

public class ClientMainActivity extends AppCompatActivity { private Button mSendString; private EditText mStingEditText; public static final int TRANSAVTION_showMessage = IBinder.FIRST_CALL_TRANSACTION; private IBinder mServer; Private Boolean isConnection = false; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isConnection = true; mServer = service; // Set up connection successfully, save IBinder object log. d("Client"," onServiceconnsuccess "); } @Override public void onServiceDisconnected(ComponentName name) { isConnection = false; }}; Private void attemptToBindService() {Intent Intent = new Intent(); intent.setClassName("com.binder.server", "com.binder.server.RemoteService"); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSendString = findViewById(R.id.btn_send_string); mStingEditText = findViewById(R.id.et_string); mSendString.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (! isConnection) { attemptToBindService(); return; } // Send data to the server process Parcel data = Parcel. Obtain (); Parcel replay = Parcel.obtain(); if (mServer ! = null) { try { data.writeString(mStingEditText.getText().toString()); Log.d("Client"," mServer.transact call"); // Send data to server process mserver. transact(TRANSAVTION_showMessage, data, replay, 0); replay.readException(); } catch (RemoteException e) { e.printStackTrace(); } finally { replay.recycle(); data.recycle(); }}}}); } @Override protected void onStart() { super.onStart(); if (! isConnection) { attemptToBindService(); }}Copy the code

Seen from the above code, the core of cross-process communication of Binder is that the client obtains the IBinder object of the server and then invokes the transact method of the object to send data to realize inter-process communication.

ADIL is used to generate related classes for interprocess communication

Join the AIDL file

interface IShowMessageAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
   void showMessage(String msg);
}

Copy the code
Modify the Client code
public class ClientMainActivityUseAidl extends AppCompatActivity { private Button mSendString; private EditText mStingEditText; private IShowMessageAidlInterface mServer; Private Boolean isConnection = false; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isConnection = true; / / call IShowMessageAidlInterface. Stub. AsInterface static method, Converts a service to a interface mServer = IShowMessageAidlInterface. Stub. AsInterface (service); Log.d("Client"," onServiceConnected success"); } @Override public void onServiceDisconnected(ComponentName name) { isConnection = false; }}; private void attemptToBindService() { Intent intent = new Intent(); intent.setClassName("com.binder.server", "com.binder.server.RemoteServiceUseAidl"); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSendString = findViewById(R.id.btn_send_string); mStingEditText = findViewById(R.id.et_string); mSendString.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (! isConnection) { attemptToBindService(); return; } try {// Call the interface showMessage method mserver.showMessage (mstingEditText.gettext ().tostring ())); } catch (RemoteException e) { e.printStackTrace(); }}}); } @Override protected void onStart() { super.onStart(); if (! isConnection) { attemptToBindService(); }}Copy the code

1. The client using IShowMessageAidlInterface Stub inner class of generated classes will receive the IBinder object is converted to an interface 2. When sending data, direct call IShowMessageAidlInterface showMessage method of interface

AsInterface method
public static com.binder.server.IShowMessageAidlInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } // Query whether the obj object is a local interface, that is, whether it is in the same process Android.os. IInterface iin = obj. QueryLocalInterface (DESCRIPTOR); if (((iin! = null) && (iin instanceof com. Binder. Server IShowMessageAidlInterface))) {if it is the same process directly return to return ((com.binder.server.IShowMessageAidlInterface)iin); } / / if it's a different process, the IBinder object using the Proxy encapsulates a layer of the return of new com. The binder. The server IShowMessageAidlInterface. Stub. The Proxy (obj); }Copy the code
The Proxy class
private static class Proxy implements com.binder.server.IShowMessageAidlInterface { 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; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ // Proxy objects encapsulate data from methods defined in the AIDL interface, Convenient to transport across processes @ Override public void showMessage (Java. Lang. String MSG) 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(msg); boolean _status = mRemote.transact(Stub.TRANSACTION_showMessage, _data, _reply, 0); if (! _status && getDefaultImpl() ! = null) { getDefaultImpl().showMessage(msg); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } public static com.binder.server.IShowMessageAidlInterface sDefaultImpl; }Copy the code

So we can know that the client use the AIDL file to generate a Stub class asInterface method, convert the received remote IBinder to IShowMessageAidlInterface interface, this interface is to realize the Proxy class, The proxy class encapsulates the incoming method data and sends it to the mRemote server.

Modify the server code
public class RemoteServiceUseAidl extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return new IShowMessageAidlInterface.Stub() { @Override public void showMessage(String msg) throws RemoteException { if (ServerMainActivity.tvShowMessage ! = null) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { ServerMainActivity.tvShowMessage.setText(msg); }}); }}}; }}Copy the code

The onBind method on the server side returns an object of the aiDL-generated Stub class, which is an abstract class where the method to be implemented is the showMessage method defined in AIDL.

public static abstract class Stub extends android.os.Binder implements com.binder.server.IShowMessageAidlInterface { private static final java.lang.String DESCRIPTOR = "com.binder.server.IShowMessageAidlInterface"; static final int TRANSACTION_showMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } @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 { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_showMessage: { data.enforceInterface(descriptor); java.lang.String _arg0; _arg0 = data.readString(); this.showMessage(_arg0); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); }}}}Copy the code

As you can see, the Sub abstract class inherits from Binder, which means that the client ends up with the Stub IBinder object. When the client calls the Tansact method, it ends up calling the Stub onTransact. Stub onTransact method according to the code to determine the customer calls the end of households which method, and then to read the received data data parsing, will deal with good data to IShowMessageAidlInterface corresponding method.

Summary: 1. Stub static methods asInterface and Proxy in AIDL generated classes are used to send data to the client; 2.Stub abstract classes are implemented by the server to receive and process data from the client