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

  service = null;

 }

};

Your ServiceConnection subclass needs to implement two methods:

1. onServiceConnected(), which is called once your activity is bound to the service

2. onServiceDisconnected(), which is called if your connection ends normally, such as you unbinding your activity from the service

Each of those methods receives a ComponentName, which simply identifies the service you connected to. More importantly, onServiceConnected() receives an IBinder instance, which is your gateway to the IPC interface. You will want to convert the IBinder into an instance of your AIDL interface class, so you can use IPC as if you were calling regular methods on a regular Java class (IWeather.Stub.asInterface(binder)).

To actually hook your activity to the service, call bindService() on the activity:

bindService(serviceIntent, svcConn, BIND_AUTO_CREATE);

The bindService() method takes three parameters:

1. An Intent representing the service you wish to invoke — for your own service, it’s easiest to use an intent referencing the service class directly (new Intent(this, WeatherPlusService.class))

2. Your ServiceConnection instance

3. A set of flags — most times, you will want to pass in BIND_AUTO_CREATE, which will start up the service if it is not already running

After your bindService() call, your onServiceConnected() callback in the ServiceConnection will eventually be invoked, at which time your connection is ready for use.

Request for Service

Once your service interface object is ready (IWeather.Stub.asInterface(binder)), you can start calling methods on it as you need to. In fact, if you disabled some widgets awaiting the connection, now is a fine time to re-enable them.

However, you will want to trap two exceptions. One is DeadObjectException — if this is raised, your service connection terminated unexpectedly. In this case, you should unwind your use of the service, perhaps by calling onServiceDisconnected() manually, as shown previously. The other is RemoteException, which is a more general-purpose exception indicating a cross-process communications problem. Again, you should probably cease your use of the service.

Prometheus Unbound

When you are done with the IPC interface, call unbindService(), passing in the ServiceConnection. Eventually, your connection’s onServiceDisconnected() callback will be invoked, at which point you should null out your interface object, disable relevant widgets, or otherwise flag yourself as no longer being able to use the service.

For example, in the WeatherPlus implementation of onServiceDisconnected() shown previously, we null out the IWeather service object.

You can always reconnect to the service, via bindService(), if you need to use it again.

Manual Transmission

In addition to binding to the service for the purposes of IPC, you can manually start and stop the service. This is particularly useful in cases where you want the service to keep running independently of your activities — otherwise, once you unbind the service, your service could well be closed down.

To start a service, simply call startService(), providing two parameters:

1. The Intent specifying the service to start (again, the easiest way is probably to specify the service class, if it’s your own service)

2. A Bundle providing configuration data, which eventually gets passed to the service’s onStart() method

Conversely, to stop the service, call stopService() with the Intent you used in the corresponding startService() call.

Catching the Lob

In Chapter 31, we showed how the service sends a broadcast to let the WeatherPlus activity know a change was made to the forecast based on movement. Now, we can see how the activity receives and uses that broadcast.

Here are the implementations of onResume() and onPause() for WeatherPlus:

@Override

public void onResume() {

 super.onResume();

 registerReceiver(receiver,

  new IntentFilter(WeatherPlusService.BROADCAST_ACTION));

}

@Override

public void onPause() {

 super.onPause();

 unregisterReceiver(receiver);

}

In onResume(), we register a static BroadcastReceiver to receive Intents matching the action declared by the service. In onPause(), we disable that BroadcastReceiver, since we will not be receiving any such Intents while paused, anyway.

The BroadcastReceiver, in turn, simply arranges to update the forecast on the UI thread:

private BroadcastReceiver receiver = new BroadcastReceiver() {

 public void onReceive(Context context, Intent intent) {

  runOnUiThread(new Runnable() {

   public void run() {

    updateForecast();

   }

  });

 }

};

And updateForecast() uses the interface stub to call into the service and retrieve the latest forecast page, also handling the case where the forecast is not yet ready (null):

private void updateForecast() {

 try {

  String page = service.getForecastPage();

  if (page==null) {

   browser.postDelayed(new Runnable() {

    public void run() {

     updateForecast();

    }

   }, 4000);

   Toast

   .makeText(this, "No forecast available", 2500).show();

  } else {

   browser.loadDataWithBaseURL(null, page, "text/html",

    "UTF-8", null);

  }

 } catch(final Throwable t) {