Выбрать главу

 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

 <application android:label="@string/app_name">

  <activity android:name=".WeatherPlus" android:label="@string/app_name">

   <intent-filter>

    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />

   </intent-filter>

  </activity>

  <service android:name=".WeatherPlusService" />

 </application>

</manifest>

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:

private void 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);

 } catch (Throwable t) {

  android.util.Log.e("WeatherPlus",

  "Exception in updateForecast()", t);

 }

}

Much of this is similar to the equivalent piece of the original Weather demo — perform the HTTP request, convert that into a set of Forecast objects, and turn those into a Web page. The first difference is that the Web page is simply cached in the service, since the service cannot directly put the page into the activity’s WebView. The second difference is that we call sendBroadcast(), which takes an Intent and sends it out to all interested parties. That Intent is declared up front in the class prologue:

private Intent broadcast = new Intent(BROADCAST_ACTION);

Here, BROADCAST_ACTION is simply a static String with a value that will distinguish this Intent from all others:

public static final String BROADCAST_ACTION =

 "com.commonsware.android.service.ForecastUpdateEvent";

Where’s the Remote? And the Rest of the Code?

In Android, services can either be local or remote. Local services run in the same process as the launching activity; remote services run in their own process. A detailed discussion of remote services will be added to a future edition of this book.

We will return to this service in Chapter 33, at which point we will flesh out how locations are tracked (and, in this case, mocked up).

CHAPTER 31

Invoking a Service

Services can be used by any application component that “hangs around” for a reasonable period of time. This includes activities, content providers, and other services. Notably, it does not include pure intent receivers (i.e., intent receivers that are not part of an activity), since those will get garbage collected immediately after each instance processes one incoming Intent.

To use a service, you need to get an instance of the AIDL interface for the service, then call methods on that interface as if it were a local object. When done, you can release the interface, indicating you no longer need the service.

In this chapter, we will look at the client side of the Service/WeatherPlus sample application. The WeatherPlus activity looks an awful lot like the original Weather application — just a Web page showing a weather forecast as you can see in Figure 31-1.

Figure 31-1. The WeatherPlus service client

The difference is that, as the emulator “moves”, the weather forecast changes, based on updates provided by the service.

Bound for Success

To use a service, you first need to create an instance of your own ServiceConnection class. ServiceConnection, as the name suggests, represents your connection to the service for the purposes of making IPC calls. For example, here is the ServiceConnection from the WeatherPlus class in the WeatherPlus project:

private ServiceConnection svcConn = new ServiceConnection() {

 public void onServiceConnected(ComponentName className,

  IBinder binder) {

  service = IWeather.Stub.asInterface(binder);

  browser.postDelayed(new Runnable() {

   public void run() {

    updateForecast();

   }

  }, 1000);

 }

 public void onServiceDisconnected(ComponentName className) {