Monday, January 7, 2008

Invoking services

Update: please check out the updated example program for Android SDK 1.5.

I was not blogging very actively in the past few days and the reason for that was ... erm ... partying. The parties seem to be over for some days so I can return to my favourite pastime of finding service-oriented architecture (SOA) traits in Android.

Activity invocation is just one way of Android's service-oriented design. Android has a complete IDL-based invocation framework built into it. I was particularly curious about it because this sort of service invocation does not have to go through the service lifecycle management that slows down activity invocation considerably.

First, about services. Similarly to intents, I was a bit surprised to find out that the same Service class is used to cover two different service abstractions.
  • If the service is started with Context.startService then it is a long-running background task whose lifecycle is not related to the lifecycle of the application that started it. Beside passing a start-up parameter bundle to this kind of service, Android platform does not provide any means of communicating with the service.
  • If the service is started with Context.bindService then the lifecycles of the service and that of the application using it are tied. The application using the service is notified about the state of the service by means of service lifecycle events. It is also possible to communicate with this type of service using the Android IDL (AIDL) framework.
As I wanted to play with AIDL, this second type of service was of interest for me. That model reminds me of OSGi and its ServiceTracker except that OSGi was much more cumbersome to program and did not not provide any client-service separation whatsoever.

  • The client requests connection to the service (by specifying an intent, as with activities). This launches the service (in separate address space)
  • Once the service is ready to accept requests, the client is notified by means of a registered listener object (conforming to the ServiceConnection interface).
  • The notification also carries the address of a stub object that can be used to invoke the service. If the service crashes or closes unexpectedly, the client is notified by the same service connection object.
  • Meanwhile, the service can be invoked through the stub objects that the aidl tool generates from the service description.
The example code can be downloaded from here.

The service description (with the extension of .aidl) is actually quite similar to a Java file. This is the AIDL of the example program.

package aexp.aidl;

// Adder service interface.

interface IAdderService {

int add( in int i1, in int i2 );
}


Currently one service can have only one interface although Dianne Hackborn from Google hopes for improvement here.

The convention of the aidl tool is that AIDL files are mixed with the Java files. This is how the tool finds them and converts them to Java stub files. In spite of the documentation's statement that only the Eclipse plugin converts AIDL to Java stub file, the Ant script generated by activityCreator invokes the aidl tool before compiling the Java file so if the AIDL is located correctly (in the Java source directory corresponding to the package declaration). In the picture below you can see IAdderService.aidl and the IAdderService.java Java stub file generated by the aidl tool.



Although AdderService is located in the same package as the activity, it has a life (and lifecycle) of its own. The service is declared in the AndroidManifest.xml file (again, XML fragment is garbled so that the blog engine does not corrupt it).

[service class=".AdderServiceImpl"/]

The service declaration could have intent filters as activities may have. In our example we target it directly, using its class name. In order to demonstrate the service lifecycle, the lifecycle events are logged.

The client is located in a separate application. The client also depends on the Java stub file generated by the service project so this file has to be copied by hand into the client project.


The interesting bit here is the AdderServiceConnection embedded class that acts as the service state listener. Just because the bindService call was issued, the service is not available! (actually, we can't obtain the stub object until the ServiceConnection object is notified by its onServiceConnected method). Note that the interfaceName argument of bindService is always null and this argument is supposed to disappear altogether.

Here is the snippet invoking the service.

private void invokeService( int i1, int i2 ) {
TextView t = (TextView)findViewById(R.id.invokeserv_result);
if( service == null )
t.setText( "Service not available" );
else {
try {
int result = service.add( i1,i2 );
t.setText( Integer.toString( result ) );
} catch( DeadObjectException ex ) {
t.setText( "Service invocation error" );
}
}
}


The code invoking the service needs to be prepared for the following exceptional cases:
  • Service is not yet available (onServiceConnected was not yet called) or has gone (onServiceDisconnected was called).
  • For some reason, the invocation was not succesful (DeadObjectException).
I expect that service invocation performance is superior to activity invocation performance but let's see that later. :-)

15 comments:

Peggy said...

I downloaded the code and tried in android-sdk_m5-rc15_windows.
I encounter a problem in invoking service


private void invokeService( int i1, int i2 ) {
TextView t = (TextView)findViewById(R.id.invokeserv_result);
if( service == null ){}
t.setText( "Service not available" );


Although bindService( i, conn, Context.BIND_AUTO_CREATE) return ture, Service is still null.

Anonymous said...

I am also getting same problem.
Service is null. How to solve it?

Gabor Paller said...

"I am also getting same problem.
Service is null. "

Please, check the updated version of the example program.

Lewis Lee said...

Dear Mr. Paller,
I'm new to Android. I have googled a lot of pages but I couldn't find useful things. Finally I found what I want. :-) This article is very helpful for me. Thanks!!! From Seoul, S.Korea.

Gabor Paller said...

Mr. Lee, if you found this article interesting, you may find this other post interesting about oneway interfaces.

Prafull Sanas said...

hi, this was a really useful post and explains the use of services and the method to create them very beautifully. Although, now while playing around with ur example a bit, i was trying to access a service from multiple applications. The problem i have is i cant use the SAME interface(AIDL) across multiple applications and thus cant talk to the service from my second activity. Is there a way this can be done? I'm able to hook onto the already running service but just cant communicate with it.. Please help..

Gabor Paller said...

What do you mean that you can't use the same AIDL from different applications? You can copy either the AIDL file or the stub compiled from it into your application then you can start using it.

Benjamin said...

thx for the very nice examples. It helps me a lot.

best regards

Ramesh Yankati said...

HI ,

Very good tutorial for AIDL.It helped me alot.
Thank you very mcuh.Enjoy AIDL.

Ramesh Yankati said...

Hi
Very good tutorial for AIDL.
Helps me alot.Thank yoy very much..

Happy ANndroid AIDL..

Marko said...

Thanks a lot!

I've was totally stuck with AIDL for a week. I tried multiple aidl tutorials but I always hit a same error. Your example worked with a simple copy pasting and setting package names correctly.

You're my savior ;)

Terry said...

Really nice article and good example. Somehow in my app on bindService, I get "Unable to start service Intent {....} not found. I tried using startService() and the service starts, then why with bind I must be facing this probem. Am stuck on this for 2 days, just can't solve out. Can you help or give any guidens, please.

Thanks

Gabor Paller said...

Terry, could you send me the code? I am curious. gaborpaller@gmail.com

Sam said...

I downloaded and imported both aidlserviceactivity and aidlserviceclient in eclipse but as soon as I compile there are lots of errors that I can't figure out how to fix them, could you please provide me with step by step instructions on how to get this to work?

Gabor Paller said...

Sam, have you downloaded the updated version? (link immediately below the post title)