To enable applications to communicate with each other, Android provides a unique implementation of IPC (Inter Process Communication) : AIDL (Android Interface Definition Language).

In simple terms, AIDL defines an interface where a client (caller) establishes a connection with a remote server through bindService. When the connection is established, it returns an IBinder object, which is the BinderProxy of the server Binder. When establishing a connection, the client uses the asInterface function to wrap the BinderProxy object in the local Proxy and assign the value to the mRemote field of the Proxy class. The local can call the remote method through the mRemote.

1. Create.aidl file

First open Android Studio and create an AIDL file. The specific code is as follows:

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
Copy the code

The basicTypes method comes with the interface, but you can only use these basic type parameters in AIDL: int, long, Boolean, float, double, String;

In addition to the basicTypes methods, we can also add our own. Therefore, you can remove the basicTypes methods and add your own.

Generate.java files

Once the method is added, select the.aidl file and choose Synchronize LocalAIDLS… from the menu that pops up. Service. Java will automatically generate the corresponding Java code for you.

After formatting the code, it looks like this:

/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/shenjiaqi/Documents/sjq/booksource/chapter6/DatabaseTest/app/src/main/aidl/com/example/databasetest/IMyAidlInterf ace.aidl */ package com.example.databasetest; public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.databasetest.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.example.databasetest.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) { return ((com.example.databasetest.IMyAidlInterface) iin); } return new com.example.databasetest.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; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.databasetest.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; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override 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 while forming. WriteString (aString); Mremote. transact(stub.transaction_basictypes, _data, _reply, 0); // The proxy holds the reference so that data can be exchanged without exposing the object. _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; }Copy the code

If you need to modify the.aidl file, select Build -> Make Project and the Java file will be regenerated.

Explanation:

For the generated Java class, many new people will not understand, here is the need to explain:

  • IMyAidlInterface: this is a servier interface that we define ourselves, that is to define the functions you want in the interface;
  • IBinder: defines a protocol to interact with remote objects and represents a cross-process transfer capability. Implement this interface to pass objects across processes, but Binder is recommended if you want to use it.
  • Binder: Implements the IBinder interface, which stands for Binder native objects. The BinderProxy class is an inner class of Binder that represents the local proxy for the Binder objects of remote processes. Both classes inherit from IBinder and thus have the ability to transfer across processes; In fact, the Binder driver automatically converts the two objects across processes.
  • Stub: AIDL, the compiler generates a static internal abstract class named Stub. This class inherits Binder, which means it is a Binder native object. It implements the IInterface interface, which means it has the capabilities promised by the Server to the Client. A Stub is an abstract class, and the implementation of a concrete IInterface needs to be implemented by the developer.
  • IInterface: IInterface represents the capabilities of the Server process object (which methods can be provided by the interface defined in AIDL file)
  • Proxy: Stub static inner class, which implements IMyAidlInterface, is a remote proxy object that can be returned to the client. When a client calls a method of proxy, it will transfer the parameters to proxy, and pass the method name and parameters to the remote real object through the remote real object it holds. Then onTransact will be called, and the corresponding method will be invoked, so as to realize cross-process invocation.

3. Transmission of complex data

If you need to pass complex data, then you need to implement the Parcelable interface, which can be serialized:

public class Info implements Parcelable { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Info() { } public Info(Parcel in) { content = in.readString(); } public static final Creator<Info> CREATOR = new Creator<Info>() { @Override public Info createFromParcel(Parcel in) { return new Info(in); } @Override public Info[] newArray(int size) { return new Info[size]; }}; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(content); Public void readFromParcel(Parcel dest) {param dest */ public void readFromParcel(Parcel dest) { Read values in the same order as in the writeToParcel() method: Content = dest.readString(); } @override public String toString() {return "content: "+ content; }}Copy the code

At the same time, create an info.aidl file to show that data can also be passed.

package com.viii.aidlclient; // This file introduces a serialized object Info for other AIDL files to use. // Note that parcelable is lowercase parcelable Info;Copy the code

This allows you to use info objects. No longer controlled by the primitive type variables above.

Create a service

Next, create a new Service to receive the message and register the Service in androidmanifest.xml:

public class MyService extends Service { private static final String TAG = "MyService"; // private MyBinder mMyBinder = new MyBinder(); @override public IBinder onBind(Intent Intent) {log. d(TAG, "onBind: "); // mBinder return null; } @Override public void onCreate() { Log.d(TAG, "onCreate: "); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: "); return super.onStartCommand(intent, flags, startId); } // This is the implementation of the server side, inheriting the stub. Stub mBinder = new IMyAidlInterface.Stub() {@override public void basicTypes(int) anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {// Implementation procedure}}; }Copy the code

At this point, the basicTypes method can be used to add concrete function code to implement the desired functionality.

After we have obtained the proxy locally, calling basicTypes triggers a call to the server.

5. Access to services

Next, bind in mainActivity.

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private IMyAidlInterface mService; private boolean mIsBound; private AdditionServiceConnection mServiceConnection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); doBindService() ; }/** * bind service */ private void doBindService() { mServiceConnection = new AdditionServiceConnection(); Intent intent = new Intent(this, MyService.class); bindService(intent, mServiceConnection, BIND_AUTO_CREATE); } /** * unbind service */ private void doUnbindService() { if (mIsBound) { unbindService(mServiceConnection); mServiceConnection = null; mIsBound = false; } } /** * ServiceConection */ class AdditionServiceConnection implements ServiceConnection { @Override public void OnServiceConnected (Interface name, IBinder Service) {// Get the local proxy when connected, so that we can call the methods in the service. mService = IMyAidlInterface.Stub.asInterface((IBinder) service); mIsBound = true; Try {// Set the death proxy service.linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { e.printStackTrace(); } Log.d(TAG, "onServiceConnected: "); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; mIsBound = false; Log.d(TAG, "onServiceDisconnected: "); DeathRecipient mDeathRecipient = new ibinder.deathrecipient () {@override public void binderDied() { if (mService == null) { return; } mService.asBinder().unlinkToDeath(mDeathRecipient, 0); mService = null; // rebind doBindService(); }}; @Override protected void onStop() { super.onStop(); doUnbindService(); }}Copy the code

With the Remote service’s Binder in hand, we can invoke methods to implement our own functionality.

At this point, an AIDL has been implemented.

Analyzing the call process

Look at the asInterface method. This is how we bind a Service to a remote Service in the onServiceConnecttion callback. What does this method do?

/** * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) { return ((com.example.databasetest.IMyAidlInterface) iin); } // In fact, the proxy object holds the real object, and after the proxy object processes the data, Then call the entity object method return new com. Example. Databasetest. IMyAidlInterface. Stub. The Proxy (obj); }Copy the code

Binder local object, Binder proxy object, BinderProxy object, BinderProxy object, BinderProxy object. It tries to find the Binder local object. If it finds the Binder local object, both Client and Server are in the same process. This parameter is the local object and is cast and returned.

If not, a remote object (in a different process) needs to be created with a Binder agent to access the remote object. Generally, if you are communicating with a remote Service object, a Binder proxy object is returned. The IBinder parameter is actually a BinderProxy.

Take a look at our implementation of aiDL’s basicTypes method; In Stub classes, basicTypes is an abstract method that we need to inherit from and implement; If Client and Server are in the same process, this method is called directly. So, if it’s a remote call, what happens? How does a Client call a Server’s method?

Calls to remote methods are done through Binder proxies, in this case the Proxy class; The basicTypes method is implemented by Proxy as follows:

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 while forming. WriteString (aString); Transact (stub.transaction_basictypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }}Copy the code

It serializes the data using a Parcel and then calls the Transact method; So what does transact do? The Proxy class is created in the asInterface method. BinderProxy means that the IBinder returned by the driver is actually BinderProxy. So the actual type of mRemote in our Proxy class should be BinderProxy; Let’s look at BinderProxy’s transact method :(binder.java inner class)

public native boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
Copy the code

This is a local method; Its implementation is in native layer, specifically in the frameworks/base/core/jni android_util_Binder. CPP files, it conducted a series of function calls, call chain is too long is not given here; Remember that it ends up calling the talkWithDriver function; As you can see from the name of this function, the communication process is left to the driver; This function is finally called by the IOCtl system. The Client process falls into kernel state, and the thread calling basicTypes hangs waiting to return. After a series of operations, the driver wakes up the Server process, calling the onTransact function of the Server process’s local object (which is actually done by the server-side thread pool). The Binder native object’s onTransact method (in this case, the Stub method) :

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; } } return super.onTransact(code, data, reply, flags); }Copy the code

Within the Server process, onTransact calls related functions based on the call number (each AIDL function has a number, and the function is not passed across processes, but the number indicates which function to call).

In this case, the basicTypes method of the Binder native object is called; This method returns the result to the driver, who wakes up the thread in the suspended Client process and returns the result. A cross-process call is then complete.

At this point, you should have some idea of the classes and roles in AIDL communication. It’s always a pattern: An object that needs to be passed across processes must inherit from IBinder. Binder native objects must inherit from Binder implementing IInterface. Proxy objects implement IInterface and hold IBinder references.

Unlike stubs, which are both binders and iInterfaces, a Proxy uses inheritance (IS) and a Proxy uses composition (HAS). They all implement all the IInterface functions.

The difference is that Stub uses policy mode to invoke virtual functions (to be subclassed), while Proxy uses composite mode. Why does a Stub use inheritance and a Proxy use composition? In fact, a Stub is an IBinder (Binder), which is itself an object that can be transported across process boundaries, so it inherits the IBinder transact function to get the ability to cross processes (given to it by the driver).

The Proxy class uses composition because it doesn’t care what it is, and it doesn’t need to transfer across processes, it just needs to have this capability, and to have this capability, it just needs to keep a reference to IBinder.

To compare this process, in feudal society, Stub is like the emperor, who can give orders to the world, he was born with the right (not to mention advocating feudal superstition). If a person also wants to command the world, he can “coerce the son of heaven to order the princes”. Why do not oneself become an emperor, one of, general circumstance is unnecessary, became an emperor actually limit also pretty much be? I can now rule the world and be free (Java inheritance); Binder, I’m not a Binder. If you had to, I might rebel. And finally, if you wanted to be emperor, it was asBinder. In the Stub class, asBinder returns this, and in the Proxy returns a reference to the holding composite class IBinder.

 

See specific example code: download.csdn.net/download/sz…