Part V

Services

Chapter 35

Services: The Theory

As noted previously, Android services are for long-running processes that may need to keep running even when decoupled from any activity. Examples include playing music even if the player activity gets garbage-collected, polling the Internet for RSS/Atom feed updates, and maintaining an online chat connection even if the chat client loses focus due to an incoming phone call.

Services are created when manually started (via an API call) or when some activity tries connecting to the service via interprocess communication (IPC). Services will live until specifically shut down or until Android is desperate for RAM and destroys them prematurely. Running for a long time has its costs, though, so services need to be careful not to use too much CPU or keep radios active too much of the time, lest the service cause the device's battery to get used up too quickly.

This chapter outlines the basic theory behind creating and consuming services. The next chapter presents a few specific patterns for services, ones that may closely match your particular needs. Hence, this chapter has limited code examples, whereas the next chapter serves up several code examples.

Why Services?

Services are a “Swiss Army knife” for a wide range of functions that do not require direct access to an activity's user interface, such as the following:

  • Performing operations that need to continue even if the user leaves the application's activities, such as a long download (e.g., downloading an app from the Android Market) or playing music (e.g., an Android music app)
  • Performing operations that need to exist regardless of activities coming and going, such as maintaining a chat connection in support of a chat application
  • Providing a local API to remote APIs, such as might be provided by a web service
  • Performing periodic work without user intervention, akin to cron jobs or Windows scheduled tasks

Even things like home screen app widgets often involve a service to assist with long-running work.

Many applications do not need any services. Very few applications need more than one. However, services are a powerful tool in an Android developer's toolbox and their functionality is a subject with which any qualified Android developer should be familiar.

Setting Up a Service

Creating a service implementation shares many characteristics with building an activity. You inherit from an Android-supplied base class, override some lifecycle methods, and hook the service into the system via the manifest.

Service Class

Just as an activity in your application extends either Activity or an Android-supplied Activity subclass, a service in your application extends either Service or an Android-supplied Service subclass. The most common Service subclass is IntentService, used primarily for the command pattern. That being said, many services simply extend Service.

Lifecycle Methods

Just as activities have onCreate(), onResume(), onPause(), and similar methods, Service implementations have their own lifecycle methods, such as the following:

  • onCreate(): As with activities, called when the service process is created, by any means
  • onStartCommand(): Called each time the service is sent a command via startService()
  • onBind(): Called whenever a client binds to the service via bindService()
  • onDestroy(): Called as the service is being shut down

As with activities, services initialize whatever they need in onCreate() and clean up those items in onDestroy(). And, as with activities, the onDestroy() method of a service might not be called if Android terminates the entire application process, such as for emergency RAM reclamation.

The warnings we've provided previously about activities being terminated abruptly in the face of low memory issues apply in a similar fashion with services. However, Android 4.0 Ice Cream Sandwich introduces a new method, onTrimMemory(), that allows the system to better handle low memory situations specifically with services, giving them a chance to release unused or unneeded resources before having to resort to onDestroy()(as covered in detail in the next chapter).

The onStartCommand() and onBind() lifecycle methods will be implemented based on your choice of communicating to the client, as will be explained later in this chapter.

Manifest Entry

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. So in the following manifest, you'll see android:name="Downloader".

If you want 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 38 for more details.

For example, here is a manifest showing the <service> element:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"images
 package="com.commonsware.android.downloader" android:versionCode="1"images
 android:versionName="1.0">
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application android:label="@string/app_name" android:icon="@drawable/cw">
        <activity android:name="DownloaderDemo" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <serviceandroid:name="Downloader"/>
    </application>
  <supports-screens android:largeScreens="true" android:normalScreens="true"images
 android:smallScreens="true" android:anyDensity="true"/>
</manifest>

Communicating to Services

Clients of services—frequently activities, though not necessarily—have two main ways to send requests or information to a service. One approach is to send a command, which creates no lasting connection to the service. The other approach is to bind to the service, establishing a bidirectional communications channel that lasts as long as the client needs it.

Sending Commands with startService()

The simplest way to work with a service is to call startService(). The startService() method takes an Intent parameter, much like startActivity() does. In fact, the Intent supplied to startService() has the same two-part role as it does with startActivity():

  • Identify the service to communicate with
  • Supply parameters, in the form of Intent extras, to tell the service what it is supposed to do

For a local service (the focus of this book), the simplest form of Intent is one that identifies the class that implements the Intent (e.g., new Intent(this, MyService.class);).

The call to startService() is asynchronous, so the client will not block. The service will be created if it is not already running, and it will receive the Intent via a call to the onStartCommand() lifecycle method. The service can do whatever it needs to in onStartCommand(), but since onStartCommand() is called on the main application thread, it should do its work very quickly. Anything that might take a while should be delegated to a background thread.

The onStartCommand() method can return one of several values, mostly to indicate to Android what should happen if the service's process is killed while it is running. The most likely return values are the following:

  • START_STICKY: The service should be moved back into the started state (as if onStartCommand() had been called), but the Intent should not be redelivered to onStartCommand()
  • START_REDELIVER_INTENT: The service should be restarted via a call to onStartCommand(), supplying the same Intent as was delivered this time
  • START_NOT_STICKY: The service should remain stopped until explicitly started by application code

By default, calling startService() not only sends the command, but tells Android to keep the service running until something tells it to stop. One way to stop a service is to call stopService(), supplying the same Intent used with startService(), or at least one that is equivalent (e.g., identifies the same class). At that point, the service will stop and will be destroyed. Note that stopService() does not employ any sort of reference counting, so three calls to startService() will result in a single service running, which will be stopped by a call to stopService().

Another possibility for stopping a service is to have the service call stopSelf() on itself. You might do this if you use startService() to have a service begin running and doing some work on a background thread, then have the service stop itself when that background work is completed.

Binding with bindService()

Binding allows a service to expose an API to activities (or other services) that bind to it. When an activity (or other client) binds to a service, it primarily is requesting to be able to access the public API exposed by that service via the service's “binder,” as returned by the service's onBind() method. When doing this, the activity can also indicate, via the BIND_AUTO_CREATE flag, to have Android automatically start up the service if it is not already running.

The service's binder is usually a subclass of Binder, on which you can put whatever methods you want to expose to clients. For local services, you can have as many methods as you want, with whatever method signatures (parameters, return type, etc.) that you want. The service returns an instance of the Binder subclass in onBind().

Clients call bindService(), supplying the Intent that identifies the service, a ServiceConnection object representing the client side of the binding, and an optional BIND_AUTO_CREATE flag. As with startService(), bindService() is asynchronous. The client will not know anything about the status of the binding until the ServiceConnection object is called with onServiceConnected(). This not only indicates the binding has been established, but, for local services, it provides the Binder object that the service returned via onBind(). At this point, the client can use the Binder to ask the service to do work on its behalf. Note that if the service is not already running and you provide BIND_AUTO_CREATE, the service will be created first before being bound to the client. If you skip BIND_AUTO_CREATE, bindService() will return false, indicating there was no existing service to bind to.

Eventually, the client will need to call unbindService(), to indicate it no longer needs to communicate with the service. For example, an activity might call bindService() in its onCreate() method, then call unbindService() in its onDestroy() method. The call to unbindService() eventually triggers onServiceDisconnected() to be called on the ServiceConnection object—at this point, the client can no longer safely use the Binder object.

If there are no other bound clients to the service, Android will shut down the service as well, releasing its memory. Hence, we do not need to call stopService() ourselves—Android handles that, if needed, as a side effect of unbinding. Android 4.0 also introduces an additional possible parameter to bindService(), called BIND_ALLOW_OOM_MANAGEMENT. Those familiar with OOM know it as the abbreviation for out of memory, and many operating systems employ an “OOM killer” to select processes for destruction in order to avert total memory exhaustion. Binding to a service with BIND_ALLOW_OOM_MANAGEMENT indicates that you consider your application and its bound service to be noncritical, allowing more aggressive consideration for it to be killed and the related service to be stopped in the event of low memory issues.

If the client is an activity, there are two important steps to take to ensure that the binding survives a configuration change, like a screen rotation:

  1. Instead of calling bindService() on the activity itself, call bindService() on the ApplicationContext (obtained via getApplicationContext()).
  2. Make sure the ServiceConnection gets from the old instance of the activity to the new one, probably via onRetainNonConfigurationInstance().

This allows the binding to persist between activity instances.

Communicating from Services

Of course, the approaches listed in the previous section work only for a client calling out to a service. The reverse is also frequently needed, so the service can let an activity or something know about asynchronous events.

Callback/Listener Objects

An activity or other service client could provide some sort of callback or listener object to the service, which the service could then call when needed. To make this work, you would need to do the following:

  1. Define a Java interface for that listener object.
  2. Give the service a public API to register and retract listeners.
  3. Have the service use those listeners at appropriate times, to notify those who registered the listener of some event.
  4. Have the activity register and retract a listener as needed.
  5. Have the activity respond to the listener-based events in some suitable fashion.

The biggest catch is to make sure that the activity retracts the listeners when it is done. Listener objects generally know their activity, explicitly (via a data member) or implicitly (by being implemented as an inner class). If the service is holding onto defunct listener objects, the corresponding activities will linger in memory, even if the activities are no longer being used by Android. This represents a big memory leak. You may wish to use WeakReferences, SoftReferences, or similar constructs to ensure that if an activity is destroyed, any listeners it registers with your service will not keep that activity in memory.

Broadcast Intents

An alternative approach, first mentioned in Chapter 21, 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. The service can call sendBroadcast(), supplying an Intent that identifies the broadcast, designed to be picked up by a BroadcastReceiver. This could be a component-specific broadcast (e.g., new Intent(this, MyReceiver.class)), if the BroadcastReceiver is registered in the manifest. Or, it could be based on some action string, perhaps even one documented and designed for third-party applications to listen for.

The activity, in turn, can register a BroadcastReceiver via registerReceiver(), though this approach will work only for Intent objects specifying some action, not ones identifying a particular component. But, when the activity's BroadcastReceiver receives the broadcast, it can do what it wants to inform the user or otherwise update itself.

Pending Results

Your activity can call createPendingResult(). This returns a PendingIntent, an object that represents an Intent and the corresponding action to be performed upon that Intent (e.g., use it to start an activity). In this case, the PendingIntent will cause a result to be delivered to your activity's implementation of onActivityResult(), just as if another activity had been called with startActivityForResult() and, in turn, called setResult() to send back a result.

Since a PendingIntent is Parcelable, and can therefore be put into an Intent extra, your activity can pass this PendingIntent to the service. The service, in turn, can call one of several flavors of the send() method on the PendingIntent, to notify the activity (via onActivityResult()) of an event, possibly even supplying data (in the form of an Intent) representing that event.

Messenger

Yet another possibility is to use a Messenger object. A Messenger sends messages to an activity's Handler. Within a single activity, a Handler can be used to send messages to itself, as was demonstrated in Chapter 20. However, between components—such as between an activity and a service—you will need a Messenger to serve as the bridge.

As with a PendingIntent, a Messenger is Parcelable, and so can be put into an Intent extra. The activity calling startService() or bindService() would attach a Messenger as an extra on the Intent. The service would obtain that Messenger from the Intent. When it is time to alert the activity of some event, the service would do the following:

  1. Call Message.obtain() to get an empty Message object.
  2. Populate that Message object as needed, with whatever data the service wishes to pass to the activity.
  3. Call send() on the Messenger, supplying the Message as a parameter.

The Handler would then receive the message via handleMessage(), on the main application thread, and thus would be able to update the UI or do whatever is necessary.

Notifications

Another approach is for the service to let the user know directly about the work that was completed. To do that, a service can raise a Notification—putting an icon in the status bar and optionally shaking, beeping, or giving some other signal. This technique is covered in Chapter 37.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.35.238