Android Core

Android Remote Service Tutorial:AIDL–how to structure the Project and lib

In this post, I will describe how to use Remote Service in Android. This kind of services are services that can be consumed by other processes using RPC (Remote Procedure Call). In the other post we talk about Local Service, in other word the application that hosts the service can consume it. AIDL Services are useful when we want to create some new functionalities and we want to distribute them as library. An interesting aspect, we should consider when developing an AIDL service is that it can be called/consumed by components running in different processes. To support IPC (Inter Process Communication), in Android we have to define an interface that describes the methods that will be exposed to the client. To create this interface we use AIDL (Android Interface Definition Language).

Considering that these remote services could be distributed as library, we have to choose what we want to give to the client (as jar) so that it can call and consume our service. It is important, then, have the right project structures so that we can create the client jar that holds only the necessary classes. In the rest of this post we will focus our attention on this aspect too.
As example, we will use the same example described last time where we get stock quotes.

Define a Remote AIDL Service

In order to create an AIDL Service we have:

  1. define and create the service interface using AIDL
  2. Implement our service and override onBind method returning our interface
  3. Define objects that the client and the server exchange and deconstruct them at low OS level so that they can be marshaled and un-marshaled. In other words, our classes have to implement Parcelable interface.
  4. Configure our service in Manifest.xml file

In our example, we know we just want to know the stock quote, so for simplicity our interface is just made by only one method called getQuote. In this method we pass the Stock pojo class that holds the information about the stock code and the values that will be filled by our service in response. Our pojo class is called Stock. So considering all things said, we have that the AIDL is:

package com.survivingwithandroid.aidlservicetutorial.service;

import com.survivingwithandroid.aidlservicetutorial.service.Stock;

interface IStockService{
    void getQuote(Stock stock);
}

notice that at line 3 we simply import the Stock definition and at line 6 we define our method. On the other hand, we have to define in AIDL our Stock pojo:

package com.survivingwithandroid.aidlservicetutorial.service;

parcelable Stock;

In this way, we defined our service interface. If you use Eclipse you can put these two files under source and the package name. Eclipse will create everything you need to use the service.

Implement the AIDL Remote Service

Now we have our interface, so we can implement the “real” Android service:

public class StockService extends Service {
    ...
 
    @Override
    public IBinder onBind(Intent intent) {
        Log.d("Srv", "OnBind");
        final ResultReceiver rec = (ResultReceiver) intent.getParcelableExtra("rec");
         
        return new IStockService.Stub() {
            @Override
            public void getQuote(Stock stock) throws RemoteException {
                (new Thread(new Worker(stock, rec))).start();
            }
        };
    }
 
 
}

As always to create a service in Android we extend the Service class provided by the SDK (line 1). The most important thing to do when we want to implement a remote service is override the onBind method and return our interface implementation (line 7). At line 9 we implement the interface method getQuote calling a thread to get the stock quote. Notice that here we used the ResultReceiver method to notify to the client the result.

Client implementation

The last step is implementing the client that calls and consumes the service. To develop the client we need:

  1. The service interface (described in AIDL)
  2. The pojo class exchanged by the client and server

With these two elements we can create our client. We will see later how to structure the project.

When we use remote service we have to “bind” our client to the remote service. We can do it, for example, in onCreate method of our Activity using bindService method:

Intent i = new Intent(IStockService.class.getName());
...
bindService(i, serviceConnection, Context.BIND_AUTO_CREATE);

where serviceConnection is a listener we some callback methods that can be used to monitor the service connection status. So we have to create a ServiceConnection instance to handle:

  1. Service connection event
  2. Service disconnection event
ServiceConnection serviceConnection = new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName name, IBinder binder) {
         Log.d("Srv", "Service connected!");
         service = IStockService.Stub.asInterface(binder);
         Log.d("Srv", "Service interface ["+service+"]");
     }

     @Override
     public void onServiceDisconnected(ComponentName name) {
         Log.d("Srv", "Service disconnected!");
         service = null;
     }
 };

At line 5, we finally get our service interface that can be used to call the remote methods on the service side. Once we have our interface we can call it as if the method is in one of our classes.

Project structure

One important aspect we have to consider when developing a remote service is what the client needs to call our service. A very simple approach is mixing pojo classes, aidl and service implementation in only one lib and distribute it to the developers that want to use our service. Even if this approach works, it has some drawbacks:

  1. The jar could have a dimension big size and the client app developers have to include it in their app distribution
  2. we are distributing as jar our service implementation and maybe it is more wise to hold it in a different place
  3. Even if we don’t modify the service interface and the pojos but only the service implementation we have the client and server jar not aligned

It is more wide, in my opinion, to structure the project in the right way so that we can distribute to the client developers only the class they really need. If we use Eclipse we can create two different project one for the server (our service implementation) and one for the client-side lib. One important this we have to remember is to mark this last one project as library: so we have

android_aidl_service_project_structu[7]

Now in the AIDLServiceLib project we add all the things the client needs:

  1. AIDL Definition
  2. Pojos references in the AIDL

android_aidl_service_project_structu[2]

while in the AIDLServiceTutorial we have:

android_aidl_service_project_structu[6]

Now it is just a matter of making a reference between these two projects, in other words the AIDLServiceTutorial uses AIDLServiceLib as library:

android_aidl_service_project_structu

Now if we want to create a client we simply create another project:

android_aidl_service_project_client2[2]

In this way, we decoupled the client lib classes to the service implementation and we can distribute the jar related to the AIDLServiceLib. The last thing to remember is set AIDLServiceLib as lib of the AIDLServiceClient.

  • Source code available soon!

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button