Senior Software Engineer & Architect. I have programmed with a variety of languages. Originally:C/C++, Mostly: Java, Occasionally: PHP, VB, Python, Unix Shell, JavaScript, Mainframe EasyTrieve and JCL. I also do Android development at the present time. Tony is a DZone MVB and is not an employee of DZone and has posted 36 posts at DZone. You can read more from them at their website. View Full User Profile

Android Interface Definition Language (AIDL) and Remote Service

02.08.2012
| 10536 views |
  • submit to reddit
In a previous article, we looked at Android Inter Process Communication (IPC) and introduced containers for fast IPC called Parcels.

We also mentioned that, for security reasons, each Android application runs in its own process, and cannot normally access the data of another application running in a different process. So mechanisms to cross process boundaries have to go through well-defined channels. To allow one application to communicate with another running in a different process, Android provides an implementation of IPC through the Android Interface Definition Language (AIDL).

The actual AIDL mechanism should be a familiar one to Java developers: you provide an interface, and a tool (the aidl tool) will generate the necessary plumbing in order for other applications (clients) to communicate with your application (service) across process boundaries. Time for some concrete example.

Say we want to implement a phone book service so that other Android applications can do a look up by name and get a list of corresponding phone numbers. We start by creating a simple Interface to express that capability, by writing a IPhoneBookService.aidl file in our source directory:

 

package com.ts.phonebook.service;

/* PhoneBook remote service, provides a list of matching phone numbers given a person's name*/
interface IPhoneBookService{

   List<String> lookUpPhone(String name);
}

As we save this .aidl file in our Android project in Eclipse, Android's aidl tool automatically generates a corresponding IPhoneBookService.java stub file in the "gen" directory of our project.  The next step is for us to actually implement our service, and we'll create a concrete PhoneBookService class. Here's the skeleton code:

 

package com.ts.phonebook.service;

import java.util.ArrayList;
import java.util.List;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class PhoneBookService extends Service{

	@Override
	public IBinder onBind(Intent intent) {
		return new IPhoneBookService.Stub() { //helper method generated by Android
		          public List<String> lookUp(String name) throws RemoteException {  // our service implementation
		        	  List<String> phoneList = new ArrayList<String>();
		        	 // populate list above by looking up by name in our phone book database
                                 //... code here

		        	 // return list of phone numbers corresponding to name parameter
		                 return phoneList;
		          }
		  };
	}

}

What we did was:

  1. Make our service extend Service and implement its onBind method, which will be called once clients send requests (i.e. bind to our service). We are using a Bound Service, which means that our phone book service will not run indefinitely in the background, but come alive and remain active only for the time needed to service client requests.
  2. The onBind method returns an IBinder, which represents our service's remote implementation. We saw earlier that Android generated  a gen/IPhoneBookService.java stub file, so what we are doing is using that generated helper method to return a concrete stub to the client that contains our service method implementation.

Our client applications can now call our lookUp method to get a list of 0, 1, or more phone numbers in our database matching the name they supplied. We have implemented our service, but we still need to register it in the AndroidManifest.xml  file of our application:

 

<application...>
   ...
   <service android:name=".PhoneBookService">
         <intent-filter>
            <action android:name="com.ts.phonebook.service.IPhoneBookService" />
        </intent-filter>
   </service>
  ...
</application>
Here we are providing the intent-filter to which our service will respond. Our phone book service is now ready. Some questions the reader may have at this point:
  1. The previous article mentioned Parcels for IPC. Where exactly does that fit in here? Where are those Parcelable objects?
  2. Do I have to use AIDL for IPC, or are there any other alternative(s)?

Here, our very basic remote service simply returns a List of String objects. String types are supported out-of-the-box by AIDL , and a List of supported elements is also supported. But suppose we want to implement a more complex remote service, one that not only returns phone numbers, but  a whole set of user data, like age, occupation, sex, salary etc... If we  want to return a custom User object with all those attributes to our callers, our User class will need to be Parcelable. 

So we would write a User class implementing the Parcelable interface the same way we did in the previous article:

 

package com.ts.userdata.service;

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable {
// code here @see previous article
}

And also write a user.aidl file containing just two lines:

 

package com.ts.userdata.service;

parcelable User;

Why a second .adl file? The AIDL contract requires that we create a separate .aidl file for each class declared as Parcelable that we wish to use in our service .

That's it.  We declared User as Parcelable, and implemented it as such so it can be marshaled/unmarshaled across process boundaries. This is what our service AIDL interface in IUserDataService.aidl will look like:

package com.ts.userdata.service;

import com.ts.userdata.service.User; // needed here

/* User data remote service, provides all available info about an individual given its id number */
interface IUserDataService{

   User lookUpUser(long userid);
}

The actual implementation will again use the generated Stub() but return a User type instead of a List of String types. Note the import statement. In the IUserDataService.aidl  file, we still need to import our User.aidl definition file even when it's in the same package.

As for the second question, the answer is yes, we can do IPC without AIDL, using a Messenger. In that case,  the client will not call methods on the service, but instead send messages to it.

We will explore the Messenger alternative in an upcoming article. We also haven't talked about the client side yet, i.e. create a client in a different application (in a separate Android project) that will communicate with our remote service.

From Tony's Blog

Published at DZone with permission of Tony Siciliani, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)