Classes Underlying AIDLGenerated Interfaces

Now let's take a look at the android.os.IInterface class. It's a base type on which all the interfaces created by AIDL are built, so they can be referenced through references of the same type. ISecondary extends IInterface.

Most of the code in the ISecondary interface is part of the definition of an abstract class called Stub. You implement remote methods by extending the Stub class. Every remote interface has this class, but because it is inside the interface created by AIDL particular to your remote methods, there is no name conflict.

The word "stub" was chosen to refer to this class because remote method systems work by creating a method on the client with the same name as the method that runs on the server. The client method is considered a "stub" because it doesn't actually carry out the operation requested; it just marshalls the data, sends it to the server, and unmar-shalls the return value. We'll show some details later in this chapter.

Implementing the Stub interface

So how do you write the code that actually implements these remote method calls? In this case, the implementation is in the class RemoteService of the ApiDemos application, and the following excerpt shows the method definitions. The first line extends the abstract class and makes a new instance of it:

private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() { public int getPid() {

return Process.myPid();

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) {

This is all you need to do to turn a method in your application into a remote method. The rest of the work of invoking the method in the other application, passing the parameters, and responding with a return value from the remote method is performed by code generated by AIDL in the Stub abstract class.

So, for a remote interface generated by AIDL, the code takes the abstract Stub class and implements the method code that will actually be used. But how does data from another process get to these methods? That is where the onTransact method comes in.

The onTransact method (see the AIDL-generated code shown earlier) is called when data in a Parcel object is delivered to a remote interface in an Android program. This method is generated by AIDL for each remote interface. In this case, it reads each argument to the method from a Parcel object, makes the method call, and writes the result to another Parcel object used for the return value of a remote method.

Parcel objects are what Java applications in Android pass to the Android IPC mechanism for moving between processes. In the simple IPC example earlier in this chapter, underlying the Context method calls used to move Intent objects between applications, the Intent object and the "extras" data associated with it are marshalled, or "flattened," into a Parcel object to be moved from one process to another and reconstituted into an Intent object with the same extras in the other process.

Basic types such as long and int are marshalled and unmarshalled by methods in the Parcel class. Other classes in the Android base classes, such as Intent and String, implement the Parcelable interface. As the name suggests, this provides an interface for the Parcel class to marshall those objects. And on top of that, implementing the Parcelable interface in your classes enables them to be marshalled, unmarshalled, and moved from one application to another.

Getting an instance of the remote Proxy object

There is one more part to this story: how does a different application find out about the interface called ISecondary, and how does the caller of the remote method actually call these methods? The answer is in the asInterface method of the Stub class, and the

Proxy class nested within Stub. And that means that any application that wants to make a remote method call must share the interface definition with the application that implements the interface. In practical terms, that means that the calling application and the application that implements the remote interface have to be compiled with the same AIDL files.

Now let's take a look at how the remote interface gets called. In the ApiDemos code we are using as an example here, this happens in the RemoteServiceBinding class, where the asInterface method is called:

mSecondaryService =

ISecondary.Stub.asInterface(service);

The parameter named service here is a reference to an IBinder interface. The Binder abstract class implements IBinder, and the Stub class (the guts of what AIDL has generated) extends Binder. Let's see how this parameter is used in the asInterface method:

public static com.example.android.apis.app.ISecondary asInterface(android.os.IBinder obj) { if ((obj == null)) { return null;

android.os.IInterface iin = (android.os.IInterface)

obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.android.apis.app.ISecondary))) { return ((com.example.android.apis.app.ISecondary) iin);

return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj);

Here the parameter is named obj, and first it is tested to see whether it is null. Then, asInterface checks to see whether there is an instance of ISecondary with the correct name. What that means is that the "remote" interface we were looking for is actually in the same application as the code calling it. And that means no inter-process communication is necessary. Otherwise, if it isn't a local interface, an instance of the Proxy object is created. Remember that this code is executing in the context of the application that wants to call the remote interface.

The Proxy class is the counterpart of the Stub abstract class. It may seem a little mind-bending that the Proxy class, which implements ISecondary, is defined inside the Stub class, which is itself inside the ISecondary interface, but it turns out to be convenient. Otherwise, more class files would have to be created by AIDL, and somehow uses of those classes managed.

Looking inside the Proxy class, we see that it has methods that have the same signature as the remote methods defined in the AIDL file. Here, unlike in the abstract class Stub, the methods are implemented, and the implementations create Parcel objects and fill them with the "flattened" parameters in exactly the right order for the onTransact method to "unflatten" them and call the remote methods.

That means an application calls a remote method by getting an instance of the Proxy class and calling the remote methods as if they were local. You can see this here, excerpted from the RemoteServiceBinding class: int pid = mSecondaryService.getPid();

Recall that mSecondaryService is returned from the ISecondary.Stub.asInterface method. Because the caller gets a Proxy object and the remote methods are implemented in a Stub object, and because both Proxy and Stub implement ISecondary, it all looks like a local method call, but the implementations of the methods are completely different in the calling application and the application that implements the remote methods.

To review:

• You define remote interfaces in AIDL. They look like Java interfaces, but are not.

• AIDL turns your remote interface definition into a Java interface with Stub and Proxy classes nested inside.

• Both the application that calls the remote method and the application that implements it use the same AIDL file and the same generated interface.

The application calling the remote interface gets an instance of the Proxy class that implements the very same interface it is defined inside of. The instance also implements "proxy" methods with the same signature as the remote methods, but they package up their parameters into a Parcel object and send them off to the application that implements the remote methods and unpackages and returns the results.

In the remote application, a concrete class extending Stub has implementations of the remote methods. The onTransact method "unflattens" data in a Parcel object, calls the remote methods and "flattens" the result, writes it into a Parcel, and sends that Parcel object back to the calling application.

However, if both the calling application and the remote service are not, in fact, remote from one another, an instance of the concrete class that implements the not-so-remote methods is used instead, cutting out the inter-process communication if it is not needed.

0 0

Post a comment