1. Overview
Simply put, AIDL defines an interface. The client (calling end) establishes a connection with the remote server through bindService. When the connection is established, an IBinder object will be returned, which is the BinderProxy of the Binder on the server. When establishing a connection, the client wraps the BinderProxy object in the local Proxy object through the asInterface function and assigns it to the mRemote field of the Proxy class, and calls the remote method locally through mRemote.
2. Create .aidl file
First open Android Studio and new 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); }
The basicTypes method comes with the interface, but it can be seen that in aidl, only these basic type parameters can be used: int, long, boolean, float, double, String;
In addition to the basicTypes method, we can also add our own methods. Therefore, you can delete the basicTypes method and add your own method.
3. Generate .java files
After adding the method, select the .aidl file and select Synchronize LocalAIDLS... in the pop-up menu, and it will automatically generate the corresponding java code for you.
After formatting the code, as follows:
package ; public interface IMyAidlInterface extends { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends implements { private static final DESCRIPTOR = ""; /** * Construct the stub at attach it to the interface. */ public Stub() { (this, DESCRIPTOR); } /** * Cast an IBinder object into an interface, * generating a proxy if needed. */ public static asInterface( obj) { if ((obj == null)) { return null; } iin = (DESCRIPTOR); if (((iin != null) && (iin instanceof ))) { return (() iin); } return new (obj); } @Override public asBinder() { return this; } @Override public boolean onTransact(int code, data, reply, int flags) throws { switch (code) { case INTERFACE_TRANSACTION: { (DESCRIPTOR); return true; } case TRANSACTION_basicTypes: { (DESCRIPTOR); int _arg0; _arg0 = (); long _arg1; _arg1 = (); boolean _arg2; _arg2 = (0 != ()); float _arg3; _arg3 = (); double _arg4; _arg4 = (); _arg5; _arg5 = (); (_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); (); return true; } } return (code, data, reply, flags); } private static class Proxy implements { private mRemote; Proxy( remote) { mRemote = remote; } @Override public asBinder() { return mRemote; } public 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, aString) throws { _data = (); _reply = (); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString);// Here is the point, proxy holds references, so that data exchange can be performed without exposing this object (Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_basicTypes = (.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, aString) throws ; }
If you need to modify the .aidl file, then after modifying it, just select build -> make project, and the corresponding java file will be regenerated.
Many people who are new to this java class will not understand. Here we need to explain:
- IMyAidlInterface: This is a servier interface we define ourselves, that is, define the functions you want to have in the interface;
- IBinder: Defines an interactive protocol with remote objects, representing a cross-process transmission capability. By implementing this interface, you can pass this object across processes. However, if you want to use it, it is recommended to inherit its subclass Binder;
- Binder:Implemented IBinder interface,It actually representsBinder Local object。 The BinderProxy class is an internal class of the Binder class, which represents the local proxy of the Binder object of the remote process; both classes inherit from IBinder, so they both have the ability to transmit across processes; in fact, when crossing the process, the Binder driver will automatically complete the conversion of these two objects.
- Stub: When AIDL, the compilation tool will generate a static internal abstract class called Stub. This class inherits Binder, indicating that it is a Binder local object, which implements the IInterface interface, indicating that it has the ability that Server promises to the Client; Stub is an abstract class, and the specific IInterface related implementation needs to be implemented by the developer himself.
- IInterface: IInterface represents what capabilities does the Server process object have (which methods can be provided, and it actually corresponds to the interface defined in the AIDL file)
- proxy:Stub's static internal class is an IMyAidlInterface interface, so it is a remote proxy object that can be used to return to the client. When the client calls a method of proxy, the parameters will be passed to proxy. Through the remote actual object it holds, the method name and parameters will be passed to the remote actual object, and then the onTransact will be called, and the corresponding method will be called to realize cross-process calls.
4. Transfer of complex data
If complex data needs to be passed, then Parcelable interface needs to be implemented, which can be serialized:
public class Info implements Parcelable { private String content; public String getContent() { return content; } public void setContent(String content) { = content; } public Info() { } public Info(Parcel in) { content = (); } 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) { (content); } /** * The parameter is a Parcel, which is used to store and transfer data * * @param dest */ public void readFromParcel(Parcel dest) { //Note that the read value order here should be consistent with the writeToParcel() method content = (); } //Easy to print data @Override public String toString() { return "content : " + content; } }
At the same time, a file must be created to indicate that the data can also be passed.
package ; //Note: The package name should be the same //The function of this file is to introduce a serialized object Info for other AIDL files to use. //Note that parcelable is lowercaseparcelable Info;
This way you can use the info object. It does not need to be controlled by the previous basic type variables.
5. Establish a service
Next, create a new service to receive messages and register the Service:
public class MyService extends Service { private static final String TAG = "MyService"; // private MyBinder mMyBinder = new MyBinder(); @Nullable @Override public IBinder onBind(Intent intent) { (TAG, "onBind: "); // should return mBinder return null; } @Override public void onCreate() { (TAG, "onCreate: "); (); } @Override public int onStartCommand(Intent intent, int flags, int startId) { (TAG, "onStartCommand: "); return (intent, flags, startId); } // This is the server implementation, inheriting the stub, what kind of capabilities do you want to implement them yourself private final mBinder = new () { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { // Specific implementation process } }; }
At this time, you can add specific function code to the basicTypes method to achieve the functions you want.
After we get the proxy locally, calling basicTypes will trigger the server's call.
6. Obtain services
Next, bind in the 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) { (savedInstanceState); setContentView(.activity_main); doBindService() ; }/** * bind service */ private void doBindService() { mServiceConnection = new AdditionServiceConnection(); Intent intent = new Intent(this, ); 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(ComponentName name, IBinder service) { // Get the local proxy when connecting, so that we can call the methods in the service. mService = ((IBinder) service); mIsBound = true; try { //Set the death proxy (mDeathRecipient, 0); } catch (RemoteException e) { (); } (TAG, "onServiceConnected: "); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; mIsBound = false; (TAG, "onServiceDisconnected: "); } } /** * Listen to whether Binder dies */ private mDeathRecipient = new () { @Override public void binderDied() { if (mService == null) { return; } ().unlinkToDeath(mDeathRecipient, 0); mService = null; //Rebind doBindService(); } }; @Override protected void onStop() { (); doUnbindService(); } }
After getting the remote service binder, we can call relevant methods to implement our own functions.
At this point, an AIDL is implemented by us.
7. Analyze the call process
Let’s take a look at the asInterface method. After we bind a Service, we get a remote service through this method in the callback of onServiceConnection. What does this method do?
/** * Cast an IBinder object into an interface, * generating a proxy if needed. */ public static asInterface( obj) { if ((obj == null)) { return null; } iin = (DESCRIPTOR); if (((iin != null) && (iin instanceof ))) { return (() iin); } // In fact, the proxy object holds the real object, and the proxy object will process the data and then call the method of the entity object. return new (obj); }
First look at the parameters of the functionIBinder
Obj of type, this object is driven to us. If it is a Binder local object, then it is Binder type. If it is a Binder proxy object, then it isBinderProxy
Type; it will try to find the Binder local object. If found, it means that the Client and Server are both in the same process. This parameter is directly the local object, directly cast the type and then return.
If it cannot be found, it means it is a remote object (in another process), then you need to create a Binder proxy object to allow this Binder proxy to achieve access to the remote object. Generally speaking, if you communicate with a remote Service object, then the returned Binder proxy object must be a Binder proxy object, and the IBinder parameter is actually BinderProxy;
Let’s take a look at our implementation of the basicTypes method of aidl; in the Stub class, basicTypes is an abstract method, we need to inherit this class and implement it; if Client and Server are in the same process, then this method is called directly; then, if it is a remote call, what happens in the middle? How does Client call the Server method?
The call to remote methods is done through the Binder proxy, in this exampleProxy
kind;Proxy
The implementation of the basicTypes method is as follows:
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, aString) throws { _data = (); _reply = (); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); // Here is the point, calling the method of the entity object (Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
It first serializes the data with Parcel and then calls the transaction method; what exactly does this transaction do? This Proxy class is created in the asInterface method. As mentioned earlier, if it is a Binder proxy, it means that the IBinder returned by the driver is actually BinderProxy. Therefore, the actual type of mRemote in our Proxy class should be BinderProxy; let's take a look at the BinderProxy's transaction method: (the inner class)
public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
This is a local method; its implementation is in the native layer, specifically in the frameworks/base/core/jni/android_util_Binder.cpp file, which makes a series of function calls, and the call chain is too long, so it will not be given here; it must be noted that it finally calls the talkWithDriver function; look at the name of this function, you can know that the communication process must be handed over to the driver to complete; this function finally passes through the ioctl system call, the Client process falls into the kernel state, and the thread that calls the basicTypes method is suspended and waits for return; the driver completes a series of operations and wakes up the Server process and calls the onTransact function of the local object of the Server process (actually completed by the Server side thread pool). Let's look at the onTransact method of the Binder local object (this is this method in the Stub class):
public boolean onTransact(int code, data, reply, int flags) throws { switch (code) { case INTERFACE_TRANSACTION: { (DESCRIPTOR); return true; } case TRANSACTION_basicTypes: { (DESCRIPTOR); int _arg0; _arg0 = (); long _arg1; _arg1 = (); boolean _arg2; _arg2 = (0 != ()); float _arg3; _arg3 = (); double _arg4; _arg4 = (); _arg5; _arg5 = (); (_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); (); return true; } } return (code, data, reply, flags); }
In the Server process, onTransact calls the related function according to the call number (each AIDL function has a number. When crossing the process, it will not pass the function, but pass the number to indicate which function to call).
In this example, the basicTypes method of the Binder local object is called; this method returns the result to the driver, which wakes up the thread in the suspended Client process and returns the result. So a cross-process call is completed.
At this point, you should have a certain understanding of the various classes and roles in the communication method of AIDL; it is always a fixed pattern: an object that needs to be passed across processes must be inherited from IBinder. If it is a Binder local object, it must inherit Binder to implement IInterface. If it is a proxy object, it will implement IInterface and hold the IBinder reference;
Proxy is different from Stub. Although they are both Binder and IInterface, the difference is that Stub adopts inheritance (is relationship), while Proxy adopts combination (ha relationship). They all implement all IInterface functions.
The difference is that Stub uses the policy pattern to call virtual functions (to be implemented by subclasses), while Proxy uses the combination pattern. Why does Stub adopt inheritance while Proxy adopt combination? In fact, Stub itself is an IBinder (Binder), which is an object that can be transmitted across process boundaries, so it must inherit IBinder to implement the transaction function to obtain the ability to cross the process (this ability is given by the driver).
The Proxy class uses combinations because it does not care about what it is, and it does not need to transmit across processes. It only needs to have this ability. To have this ability, it only needs to retain a reference to IBinder.
The above is the detailed content of analyzing the examples and principles of Android AIDL. For more information about Android AIDL, please follow my other related articles!