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.)
• String
and CharSequence
• List
and Map
(from java.util
)
• Any other AIDL-defined interfaces
• Any Java classes that implement the Parcelable
interface, 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 inout
can 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 .aidl
extension 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 aidl
utility 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 .Stub
class (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 Service
subclass
For example, here is the IWeather.Stub
instance:
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.xml
file, for it to be recognized as an available service for use. That is simply a matter of adding a service
element as a child of the application
element, providing android:name
to reference your service class.
For example, here is the AndroidManifest.xml
file 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:permission
attribute 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 Intent
filters, 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 WeatherPlusService
implementation 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);
Читать дальше