Android AIDL and Remote Client
Join the DZone community and get the full member experience.
Join For Freein the previous article , we described how to set up a remote service using aidl. before that, in a first article , we showed an implementation of a custom parcelable user class to pass across processes. now, we're going to look at the client and finally produce a little concrete example on the phone. talking about concepts is all fine and dandy, but as engineers, we need to have some sort of working software in the end. an aidl client/service .zip file will be included at the end of this article for download, so the reader can play around with the code.
since we need to test access to the service from a different process , we start by creating a separate android project from our service. we'll need a very basic ui so that we can test our remote service. therefore, our client must be an activity . our client must also be able to:
- connect to the service, i.e. bind to it using a serviceconnection
- get the data it needs using the generated service stub
our basic client will have the following skeleton:
package com.ts.dataclient; import android.app.activity; import android.content.serviceconnection; // etc... // import the service aidl files import com.ts.userdata.user; import com.ts.userdata.iuserdataservice; /** * client screen to bind to userdataservice * */ public class dataclientactivity extends activity { /** service to which this client will bind */ iuserdataservice dataservice; /** connection to the service (inner class) */ dataconnection conn; // etc... }
and we'll implement our
serviceconnection
as an inner class of the client
activity
:
/** inner class used to connect to userdataservice */ class dataconnection implements serviceconnection { /** is called once the bind succeeds */ public void onserviceconnected(componentname name, ibinder service) { dataservice = iuserdataservice.stub.asinterface(service); } /*** is called once the remote service is no longer available */ public void onservicedisconnected(componentname name) { // dataservice = null; } }
the
stub.asinterface
method was one of the automatically generated methods by the aidl tool when we set up our service. the rest is pretty much self-documenting.
next, we'll implement a couple of activity lifecycle methods for the client:
/** called when the activity is first created */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); // connect to the service conn = new dataconnection(); // name must match the service's intent filter in the service manifest file intent intent = new intent("com.ts.userdata.iuserdataservice"); // bind to the service, create it if it's not already there bindservice(intent, conn, context.bind_auto_create); } /** clean up before activity is destroyed */ @override protected void ondestroy() { super.ondestroy(); unbindservice(conn); dataservice = null; }
next, we'll create a very basic ui consisting of a field and a submit button. here are the most relevant parts:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" ...> ... <edittext android:id="@+id/uid" ../> <button android:text="submit" android:onclick="getdata" ../> </linearlayout>
then we need to get the actual data we want from the remote service. on submit (
onclick
handler), we display a toast message with the data :
/** handler for submit button */ public void getdata(view v){ user usr = null; // user is parcelable @see first article try { // get user input edittext userid = (edittext) findviewbyid(r.id.uid); long id = long.parselong( userid.gettext().tostring().trim() ); // call the aidl service usr = dataservice.lookupuser(id); } catch(numberformatexception nfex){ toast.maketext(this, "id must be a number.", toast.length_short).show(); } catch (remoteexception e) { toast.maketext(this, "service unavailable", toast.length_short).show(); } if(usr != null){ toast.maketext(this, usr.tostring(), toast.length_long).show(); } }
we're almost done. back to our service in the
previous article
, we'll add a few lines of code to return something concrete to clients, so we can test that it all works:
package com.ts.userdata; import com.ts.userdata.iuserdataservice; // other imports here /** laughably simple service implementation. we'll return some data if user enters id=101. * for all other values, we return a non-registered, empty set */ public class userdataservice extends service { @override public ibinder onbind(intent arg0) { return new iuserdataservice.stub() { // generated by the system public user lookupuser(long id) throws remoteexception { // test user usr = new user(); usr.setid(101l); usr.setage(30); usr.setphone("123 456 7890"); usr.setregistered(true); // anonymous user user anonym = new user(); anonym.setphone("not published"); anonym.setregistered(false); return ( id == usr.getid() ? usr : anonym ); } }; } }
the above implementation is of course hidden to the clients, and is not distributed to anyone outside the service. however, the client needs access to the aidl files and also the
parcelable user
class (more on that later) to be able to use the service. so we need to copy those
with their original package structure
to the client project. for our demonstration purposes, we'll deploy both applications on the phone at the same time just by deploying the client. the easiest way to do that is to include the service application in the client application's build path (in eclipse, go to
project/properties/java build path/
and add the service project). once we do that and deploy, here's what we get on the actual phone:
![]() |
![]() |
the ipc worked. we have effectively communicated across processes, i.e. android applications.
note that we have to distribute not only the aidl files, but also our custom parcelable user class to our clients since they need to get it at the other end of ipc channel. that could be a problem because user is not an interface. if we ever need to change its implementation, we will break our existing clients. a simpler and more stable aidl service would be one that only uses types provided by the system that are supported out-of-the-box, like our phone lookup service in the previous article, which returned a list of string types. so the final word is, keep those issues in mind when deciding to create your own parcelable classes in aidl.
aidl client/service .zip file: android-aidl.zip
Opinions expressed by DZone contributors are their own.
Comments