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.
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:
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.
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.
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
.
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 meansonStartCommand()
: 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 downAs 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.
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"
package="com.commonsware.android.downloader" android:versionCode="1"
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"
android:smallScreens="true" android:anyDensity="true"/>
</manifest>
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.
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()
:
Intent
extras, to tell the service what it is supposed to doFor 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 timeSTART_NOT_STICKY
: The service should remain stopped until explicitly started by application codeBy 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 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:
bindService()
on the activity itself, call bindService()
on the ApplicationContext
(obtained via getApplicationContext()
).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.
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.
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:
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 WeakReference
s, SoftReference
s, 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.
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.
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.
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:
Message.obtain()
to get an empty Message
object.Message
object as needed, with whatever data the service wishes to pass to the activity.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.
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.