Services will tend to offer inter-process communication (IPC) as a means of interacting with activities or other Android components. Each service declares what methods it is making available over IPC; those methods are then available for other components to call, with Android handling all the messy details involved with making method calls across component or process boundaries.
The core of this, from the standpoint of the developer, is expressed in AIDL: the Android Interface Description Language. If you have used IPC mechanisms like COM, CORBA, or the like, you will recognize the notion of IDL. AIDL describes the public IPC interface, and Android supplies tools to build the client and server side of that interface.
With that in mind, let’s take a look at AIDL and IPC.
IDLs are frequently written in a “language-neutral” syntax. AIDL, on the other hand, looks a lot like a Java interface. For example, here is the AIDL for the IWeather:
packagecom.commonsware.android.service;
// Declare the interface.
interfaceIWeather {
String getForecastPage();
}
As with a Java interface, you declare a package at the top. As with a Java interface, the methods are wrapped in an interface declaration ( interface IWeather { ... }). And, as with a Java interface, you list the methods you are making available.
The differences, though, are critical.
First, not every Java type can be used as a parameter. Your choices are:
• Primitive values ( int, float, double, boolean, etc.)
• Stringand CharSequence
• Listand Map(from java.util)
• Any other AIDL-defined interfaces
• Any Java classes that implement the Parcelableinterface, which is Android’s flavor of serialization
In the case of the latter two categories, you need to include import statements referencing the names of the classes or interfaces that you are using (e.g., import com.commonsware.android.ISomething). This is true even if these classes are in your own package — you have to import them anyway.
Next, parameters can be classified as in, out, or inout. Values that are out or inoutcan be changed by the service and those changes will be propagated back to the client. Primitives (e.g., int) can only be in; we included in for the AIDL for enable()just for illustration purposes.
Also, you cannot throw any exceptions. You will need to catch all exceptions in your code, deal with them, and return failure indications some other way (e.g., error code return values).
Name your AIDL files with the .aidlextension and place them in the proper directory based on the package name.
When you build your project, either via an IDE or via Ant, the aidlutility from the Android SDK will translate your AIDL into a server stub and a client proxy.
Given the AIDL-created server stub, now you need to implement the service, either directly in the stub, or by routing the stub implementation to other methods you have already written.
The mechanics of this are fairly straightforward:
• Create a private instance of the AIDL-generated .Stubclass (e.g., IWeather.Stub)
• Implement methods matching up with each of the methods you placed in the AIDL
• Return this private instance from your onBind()method in the Servicesubclass
For example, here is the IWeather.Stubinstance:
private finalIWeather.Stub binder = newIWeather. Stub() {
publicString getForecastPage() {
return( getForecastPageImpl());
}
};
In this case, the stub calls the corresponding method on the service itself. That method, which simply returns the cached most-recent weather forecast for the current location, is shown here:
synchronized privateString getForecastPageImpl() {
return(forecast);
}
Note that AIDL IPC calls are synchronous, and so the caller is blocked until the IPC method returns. Hence, your services need to be quick about their work.
Finally, you need to add the service to your AndroidManifest.xmlfile, for it to be recognized as an available service for use. That is simply a matter of adding a serviceelement as a child of the applicationelement, providing android:nameto reference your service class.
For example, here is the AndroidManifest.xmlfile for WeatherPlus:
package="com.commonsware.android.service">
Since the service class is in the same Java namespace as everything else in this application, we can use the shorthand dot-notation ( ".WeatherPlusService") to reference our class.
If you wish to require some permission of those who wish to start or bind to the service, add an android:permissionattribute naming the permission you are mandating — see Chapter 35 for more details.
Lobbing One Over the Fence
Classic IPC is one-way: the client calls functions on the service. It is possible, through the creative use of AIDL, to allow the service to call back into an activity. However, this is a bit fragile, as the service may not know if the activity is still around or if it has been killed off to free up some memory.
An alternative approach, first mentioned in Chapter 23 which discusses Intentfilters, is to have the service send a broadcast Intent that can be picked up by the activity… assuming the activity is still around and is not paused. We will examine the client side of this exchange in Chapter 31; for now, let us examine how the service can send a broadcast.
The theory behind the WeatherPlusServiceimplementation is that the service gets “tickled” when the device (or emulator) position changes. At that point, the service calls out to the Web service and generates a new forecast Web page for the activity to display. At the same time, though, the service also sends a broadcast, to alert the activity that there is a page update available if it wants it.
Here is the high-level implementation of the aforementioned flow:
privatevoid updateForecast(Location loc) {
String url = String. format(format, loc. getLatitude(),
loc. getLongitude());
HttpGet getMethod = new HttpGet(url);
try{
ResponseHandlerString responseHandler = new BasicResponseHandler();
String responseBody = client. execute(getMethod, responseHandler);
String page = generatePage( buildForecasts(responseBody));
synchronized( this) {
forecast = page;
}
sendBroadcast(broadcast);
Читать дальше