preface
Following the brief introduction of AIDL in the last article, I believe that WE should have a general understanding of AIDL. So in this article, we will deeply discuss why AIDL can complete this cross-process operation and whether there are some unknown secrets hidden in it. Let us follow the author’s ideas and slowly remove the mystery shrouded in AIDL.
The profile
The figure above is used to describe the work flow of AIDL from Client to Server. Binder is the core of AIDL
The anatomy of the
asInterface
The Binder object on the server is converted into an AIDL interface type object required by the client. This process is process-specific (if the client and server are in the same process, the Stub object on the server is returned; otherwise, the system encapsulated stub.proxy object is returned).
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) { IDemandManager demandManager = IDemandManager.Stub.asInterface(service); }};Copy the code
In ServiceConnection binding service method, we through IDemandManager. The Stub. AsInterface (service) method to obtain IDemandManager object, we followed asInterface pace and see what’s inside.
As you can see from the class structure diagram above, the idemandManager.aidl file is compiled into an interface class whose core members are Stub classes and their internal Proxy class. If the process is the same as the DESCRIPTOR, the Stub object itself (obj.queryLocalInterface(DESCRIPTOR)) is returned. Otherwise, if it is cross-process, the Stub’s Proxy inner class is returned. This means that the asInterface method returns the capabilities of a remote interface. In our case, the asInterface capabilities are get/setDemand and register/unbind listening interfaces.
asBinder
Following asInterface, we see a neat method, asBinder
As the name implies, asBinder is used to return the current Binder object.
//Stub
@Override
public android.os.IBinder asBinder() {
return this;
}Copy the code
//Proxy
private android.os.IBinder mRemote;
@Override
public android.os.IBinder asBinder() {
return mRemote;
}Copy the code
According to the code we can be traced back to, the proxy service (bindService) when the mRemote is binding by IDemandManager. Stub. AsInterface (binder); The IBinder object passed in.
Binder objects have cross-process capabilities. They are native Binder objects in Stub classes and remote Proxy objects in Proxy classes. [So the cross-process puzzle will become clearer with the analysis and research of Binder.]
Since our compiled IDemandManager interface inherits the Android.os. IInterface interface, we will first examine the IInterface.
public interface IDemandManager extends android.os.IInterfaceCopy the code
IInterface
The IInterface declares only one method, but the Stub and Proxy implement this interface indirectly, respectively.
/** * Base class for Binder interfaces. When defining a new interface, * you must derive it from IInterface. */
public interface IInterface
{
/** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */
public IBinder asBinder(a); }Copy the code
Binder Base class of an interface. When defining a new interface, you must implement the IInterface interface.
Retrieves the Binder objects associated with this interface. You must use it instead of a simple conversion so that the proxy object can return the correct result.
From the above system notes, we can understand:
- To declare (or manually diy create) an AIDL-like interface, you inherit
IInterface
- Represents the capabilities that the remote Server object has, specified by
Binder
Express that ability.
DESCRIPTOR
private static final java.lang.String DESCRIPTOR = "qdx.aidlserver.IDemandManager";// System generatedCopy the code
A unique identifier for a Binder, typically the current Binder class name.
OnTransact (server receiving)
We found that the IDemandManager interface actually doesn’t have a lot of complicated methods. After looking at the asInterface and asBinder methods, let’s look at the onTransact method.
The onTransact method runs in the server’s Binder thread pool, where remote requests are processed by the underlying system wrapper when the client makes a cross-process request. If this method returns false, the client’s request will fail.
- Code: Determines what the target method of the client request is. (The getDemand or setDemandIn method in the project)
- Data: Fetches the parameters required by the target method from data, if it has any.
- Reply: When the target method completes execution, if the target method has a return value, write the return value to reply.
- Flag: Additional operation flags. Either 0 for a normal RPC, or FLAG_ONEWAY for a one-way RPC.
In other words, the onTransact method is the core of the server’s processing, receiving the request from the client and returning the result after executing the server’s method with the parameters carried by the client. Below, through the code generated by the system, we briefly analyze the setDemandIn and setDemandOut methods written by our project in the onTransact method.
case TRANSACTION_setDemandIn: {
data.enforceInterface(DESCRIPTOR);
qdx.aidlserver.MessageBean _arg0;
if ((0! = data.readInt())) {
_arg0 = qdx.aidlserver.MessageBean.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.setDemandIn(_arg0);
reply.writeNoException(a);
return true;
}
case TRANSACTION_setDemandOut: {
data.enforceInterface(DESCRIPTOR);
qdx.aidlserver.MessageBean _arg0;
_arg0 = new qdx.aidlserver.MessageBean(a);
this.setDemandOut(_arg0);
reply.writeNoException(a);if ((_arg0 ! = null)) { reply.writeInt(1);
_arg0.writeToParcel(reply,android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}Copy the code
This code is not much mystery, is responsible for reading and writing data, as well as the result of the return. In the setDemandIn method, we can see that we read the data parameter data passed from the client. In the setDemandIn method, we can read the data parameter data. Namely, client -> server. The OUT method can analyze itself in the same way. In,out,inout analysis in AIDL
Transact (client call)
After analyzing the Stub, we were left with the Stub’s internal Proxy class, which we were surprised to discover was primarily used for method calls, that is, cross-process calls from clients.
The transact method runs on the client side. It first creates an input Parcel object (_data) and an output Parcel object (_reply) for the method. The transact method of the IBinder object from the binding service is then called to initiate a remote procedure call (RPC) request while the current thread is suspended; The onTransact method on the server side is then called until the RPC procedure returns, and the current thread continues to execute and retrieve the return result of the RPC procedure from _REPLY, which returns the data in _reply.
So in cross-process communication, a Parcel is the basic unit of communication, the delivery carrier. The transact method is a native method implemented in the native layer. After the analysis, I felt a lot of insight. Once again, I quoted the picture outlined at the beginning to see the general idea, and the whole idea floated in my mind. So dead
conclusion
Through the analysis of AIDL, we found that all of these are organized around Binder. AIDL is used to receive and process data through Stub class, and Proxy class is used to send data, and these two classes are only used to process and call Binder. In the next chapter, we will find out what Binder is. Can easily navigate across processes.
So the so-called server side and client side, when we look at the original is not the same kind of processing
Stubs act as servers and hold Binder entities (local objects).
- Get the data from the client and perform the corresponding operation based on the method ID.
- Fetch the data and call the corresponding method written locally.
- The data that needs to be sent back is written to the Reply stream and sent back to the client.
Proxy The Proxy class acts as a client and holds Binder references (handles).
- Generate _DATA and _REPLY data streams and store client data to _data.
- They are passed to the server through the Transact () method and requested to invoke the specified method.
- Receive the _REPLY data stream and retrieve the data from the server.
And the so-called server and Client are relative, the server can not only receive and process messages, and can regularly send data to the Client, at the same time, the server uses Proxy class cross-process invocation, equivalent to acting as a “Client”. It is also important to understand that when communicating across processes, the data objects are not sent from process A to process B verbatim. Cook says he’s giving you Apple, but the iPhone X you received was actually made in America? No, it could also be made in China.
As mentioned above, the AIDl tool has been analyzed. If there are shortcomings in the article, please point out. If you have a better opinion can also leave a message, the ultimate goal is to progress together. Android Binder application Layer summary and analysis for those interested.
Finally, thanks to the professional analysis of Android Development Art Exploration.