Talk to the interviewer about Binder (2) and the interviewer about binder
The interviewer said, "as you said just now, in the Android system, Binder is used for inter-process communication. How can I hear that AIDL is also used for inter-process communication? ".
In fact, AIDL is only a descriptive Language. Its full name is Android Interface Definition Language, that is, the Interface Definition Language. With ADT, we can convert the custom AIDL file into Java code, these codes can be used for inter-process communication (IPC ). Why can these codes be used for IPC? This is because the Code defines the Binder object used as the server in the Binder mechanism and the Proxy object used in the client.
As mentioned in the previous article, as a service on the server, a Binder object will be created in this thread and Its Reference will be passed to the Binder driver, when the client obtains the reference of the corresponding Binder object through the Binder driver, it actually obtains a Proxy object, and then it uses this Proxy object to follow the driver, the driver then communicates with the Binder object on the server.
With the code generated by AIDL, we can help us better understand the role of the Binder mechanism at the code level.
It seems that the interview has become a machine operation...
We should first write an AIDL file to define an interface and corresponding methods.
First, why do I define interfaces? Think about it, in fact, communication in different processes is nothing more than to use each other's services, and a very good form of service is programming interfaces, that is, we only need to know what the name provided by the Service is and what parameters are needed, but we do not care about how the specific server is implemented. When the server changes the implementation, it only needs to maintain interface consistency, and will not affect the use of the client.
package com.lms.aidl;import java.util.List;import com.lms.aidl.Bean;interface ITestService {List<Bean> getBean();void addBean(in Bean bean);}
It looks like an interface in Java.
Using the ADT plug-in, the following java code is generated in Eclipse:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: ...\\AidlServer\\src\\com\\lms\\aidl\\ITestService.aidl */package com.lms.aidl;public interface ITestService extends android.os.IInterface {/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implementscom.lms.aidl.ITestService {private static final java.lang.String DESCRIPTOR = "com.lms.aidl.ITestService";/** Construct the stub at attach it to the interface. */public Stub() {this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.lms.aidl.ITestService interface, * generating a proxy if needed. */public static com.lms.aidl.ITestService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.lms.aidl.ITestService))) {return ((com.lms.aidl.ITestService) iin);}return new com.lms.aidl.ITestService.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic 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_getBean: {data.enforceInterface(DESCRIPTOR);java.util.List<com.lms.aidl.Bean> _result = this.getBean();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_addBean: {data.enforceInterface(DESCRIPTOR);com.lms.aidl.Bean _arg0;if ((0 != data.readInt())) {_arg0 = com.lms.aidl.Bean.CREATOR.createFromParcel(data);} else {_arg0 = null;}this.addBean(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.lms.aidl.ITestService {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.lms.aidl.Bean> getBean()throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.lms.aidl.Bean> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getBean, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.lms.aidl.Bean.CREATOR);} finally {_reply.recycle();_data.recycle();}return _result;}@Overridepublic void addBean(com.lms.aidl.Bean bean)throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);if ((bean != null)) {_data.writeInt(1);bean.writeToParcel(_data, 0);} else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_addBean, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_getBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}public java.util.List<com.lms.aidl.Bean> getBean()throws android.os.RemoteException;public void addBean(com.lms.aidl.Bean bean)throws android.os.RemoteException;}
Let's take a closer look at this code. We can see that three classes are defined:
1) interface ITestService and its corresponding method
2) Stub class
3) Proxy class
The Stub class is an abstract class that inherits the Binder class and implements the ITestService. In fact, this is the Binder class that needs to be implemented when we define a server service. That is to say, when we create a service and want it to be used across processes, we can implement such a Stub class in our service and implement the corresponding logic in the methods defined by the Stub class. This example will be provided in the next article.
In the Stub class, another Proxy class is defined, and the ITestService interface is also implemented. Therefore, for client processes, it communicates with the Proxy, it feels like the Binder class communication on the server, because the methods exposed on both sides are the same, which is also a very typical application of the proxy mode in the design mode.
In fact, the Proxy class interacts with the driver through an IBinder-type mRemote object, and interacts the corresponding data information with the server process through the driver, while the Android Binder class, the IBinder interface is actually implemented as follows:
public class Binder implements IBinder { /* * Set this flag to true to detect anonymous, local or member classes * that extend this Binder class and that are not static. These kind * of classes can potentially create leaks. */ private static final boolean FIND_POTENTIAL_LEAKS = false; private static final String TAG = "Binder"; private int mObject;
So here, the Proxy class can treat the mRemote object as the Binder object of the server. Communication with the mRemote object is equivalent to communication with the Binder object of the server.
After so many times, the server uses the Stub class, that is, the Binder object. The client uses the Proxy class. Although the Proxy class also uses the Binder interface of mRemote, in A specific process, for example, process A, how does it determine which object to take? Is it the Stub class or the Proxy class?
This can also be seen from the above Code! The interviewer seems a little interesting !!!!
In Stub class, we can see the following method:
/** * Cast an IBinder object into an com.lms.aidl.ITestService interface, * generating a proxy if needed. */public static com.lms.aidl.ITestService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.lms.aidl.ITestService))) {return ((com.lms.aidl.ITestService) iin);}return new com.lms.aidl.ITestService.Stub.Proxy(obj);}
This is to convert an IBinder interface object into an interface object we define. Here, we can find that when a process converts an IBinder interface object to an interface, it will first use the queryLocalInterface method of the IBinder object to obtain whether there is a local interface object, that is, the Stub object. If there is no, it will create a Proxy object? Why? If it cannot be found, it indicates that the Binder object is not in the current process, that is, inter-process communication is required. You are a different process. Of course, you must create a Proxy object, this is what I understand. Although it is very difficult to say and dizzy, I think it should be right.
Or, let's look at the queryLocalInterface method in the Binder class,
public IInterface queryLocalInterface(String descriptor) { if (mDescriptor.equals(descriptor)) { return mOwner; } return null; }
This descriptor is a constant automatically generated in the code we created. In fact, it specifies the descriptor of the current service. If they are the same, mOwner is returned, and mOwner is an instance of the Service implementing this interface, which is defined in the attachInterface method, as follows:
public void attachInterface(IInterface owner, String descriptor) { mOwner = owner; mDescriptor = descriptor; }
From the Stub constructor, we can see that the owner is a Stub object we implement, right.
/** Construct the stub at attach it to the interface. */public Stub() {this.attachInterface(this, DESCRIPTOR);}
Furthermore, in the Stub class, an identifier is also defined for the defined interface method, as follows:
static final int TRANSACTION_getBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
With these identifiers, you can use the onTransact method to obtain the data in the driver to know which method the client wants to use on the server.
Ah! I almost forgot. What is the onTransact method?
Actually, from the code generated by the above AIDL, we can see that the onTransact method is mainly used to exchange data between the user space and the kernel space, that is to say, to implement data interaction between processes to notify each other of what to do.
The transmitted data is in the Parcel parameter data and reply. For this, I think it is nothing more than the Protocol and specification set for data interaction, reading order, and so on, I have to add more knowledge to make it clear.
It was too late, and the interviewer fell asleep ....
To sum up a little, the AIDL file is actually a secondary file, because it can play a role with the existence of ADT, because ADT can be based on the AIDL file we have defined, generate Code related to the Binder mechanism, such as Stub and Proxy.
Therefore, for some Daniel, you can directly write the code generated by ADT without the AIDL file and implement inter-process communication. Therefore, AIDL, it is just a tool that simplifies IPC development. In fact, it has nothing to do with IPC itself.
Of course, AIDL cannot be used to implement IPC. At most, AIDL and ADT can be used to implement the Binder Mechanism and thus implement IPC.