A service within the Android Software Development Kit (SDK) can mean one of two things. First, it can mean a background process, performing some useful operations at regular intervals. Second, a service can be an interface for a remote object, called from within your application. In both cases, the service object extends the Service
class from the Android SDK, and it can be a stand-alone or part of an application with a complete user interface.
In this chapter, you learn how to create and interact with an Android service. You also learn how to define a remote interface using the Android Interface Definition Language (AIDL). Finally, you learn how to pass objects through this interface by creating a class that implements a Parcelable
object.
Creating an Android service involves extending the Service
class and adding a service block to the AndroidManifest.xml
permissions file. The Service
class overrides the onCreate()
, onStart()
, and onDestroy()
methods to begin with. Defining the service name allows other applications to start the service that runs in the background and stop it.
For this example, we implement a simple service that generates a GPX track file, logging location information at regular intervals when the service runs. The following code gives a simple definition to the Service
class called GPXService
:
You need to understand the lifecycle of a service because it’s different from that of an activity. If a service will be started by the system with a call to the Context.StartService()
method, the onCreate()
method is called just before the onStart()
method. However, if the service will be bound to with a call to the Context.bindService()
method, the onCreate()
method will be called just before the onBind()
method. The onStart()
method will not be called in this case. We talk more about binding to a service in a moment. Finally, when the service is finished—that is, it is stopped and no other process is bound to it, the onDestroy()
method is called. Everything for the service must be cleaned up in this method.
With this in mind, here is the full implementation of the onCreate()
method for the GPXService
class previously introduced:
Because the object doesn’t yet know if the next call will be to the onStart()
method or the onBind()
method, we make a couple of quick initialization calls, but no background processing is started. Even this might be too much if neither of these objects were used by the interface provided by the binder.
Next, let’s look at the onStart()
method in greater detail.
The onStart()
method is where the background processing starts. In this example, though, the background processing is actually just registering for an update from another service. For more information about using Location-based services and the LocationManager
, see Chapter 11, “Using Location-Based Services (LBS) APIs,” and for more information on Notification
calls, see Chapter 16, “Working with Notifications.”
The use of a callback to receive updates is recommended over doing background processing to poll for updates. Most mobile devices have limited battery life. Continual running in the background, or even just polling, can use a substantial amount of battery power. In addition, implementing callbacks for the users of your service is also more efficient for the same reasons.
In this case, we turn on the GPS for the duration of the process, which might impact battery life even though we request a lower power method of location determination. Keep this in mind when developing services.
The Intent
extras object retrieves data passed in by the process requesting the service. Here, we retrieve one value, EXTRA_UPDATE_RATE
, for determining the length of time between updates. The string for this, update-rate
, must be published externally, either in developer documentation or in a publicly available class file so that users of this service know about it.
The implementation details of the LocationListener
object, trackListener
, are not interesting to the discussion on services. However, processing should be kept to a minimum to avoid interrupting what the user is doing in the foreground. Some testing might be required to determine how much processing a particular phone can handle before the user notices performance issues.
There are two common methods to communicate data to the user. The first is to use Notifications
. This is the least-intrusive method and can be used to drive users to the application for more information. It also means the users don’t need to be actively using their phone at the time of the notification because it is queued. For instance, a weather application might use notifications to provide weather updates every hour.
The other method is to use Toast
messages. From some services, this might work well, especially if the user expects frequent updates and those updates work well overlaid briefly on the screen, regardless of what the user is currently doing. For instance, a background music player could briefly overlay the current song title when the song changes.
The onDestroy()
method is called when no clients are bound to the service and a request for the service to be stopped has been made via a call to the Context.stopService()
method, or a call has been made to the stopSelf()
method from within the service. At this point, everything should be gracefully cleaned up because the service ceases to exist.
Here is an example of the onDestroy()
method:
Here, we stop updates to the LocationListener
object. This stops all our background processing. Then, we notify the user that the service is terminating. Only a single call to the onDestroy()
method happens, regardless of how many times the onStart()
method is called.
The system will not know about a service unless it is defined within the AndroidManifest.xml
permissions file using the service tag.
This block of XML defines the service name, GPXService
, and that the service is enabled. Then, using an Intent
filter, we use the same string that we defined within the class. This is the string that is used later on when controlling the service. With this block of XML inside the application section of the manifest, the system now knows that the service exists and it can be used by other applications.
At this point, the example code has a complete implementation of a Service
. Now we write code to control the service we previously defined.
Starting a service is as straightforward as creating an Intent
with the service name and calling the startService()
method. In this example, we also set the update-rate Intent
extra parameter to 5 seconds. That rate is quite frequent but works well for testing. For practical use, we’d probably want this set to 60 seconds or more. This code triggers a call to the onCreate()
method, if the Service
isn’t bound to or running already. It also triggers a call to onStart()
method, even if the service is already running.
Later, when we finish with the service, it needs to be stopped using the following code:
This code is essentially the same as starting the service but with a call to the stopService()
method. This calls the onDestroy()
method if there are no bindings to it. However, if there are bindings, onDestroy()
will not be called until those are also terminated. This means background processing might continue despite a call to the stopService()
method. If there is a need to control the background processing separate from these system calls, a remote interface will be required.
Sometimes it is useful to have more control over a service than just system calls to start and stop its activities. However, before a client application can bind to a service for making other method calls, the interface needs to be defined. The Android SDK includes a useful tool and file format for remote interfaces for this purpose.
To define a remote interface, you must declare the interface in an AIDL file, implement the interface, and then return an instance of the interface when the onBind()
method is called.
Using the example GPXService
service we already built in this chapter, we now create a remote interface for it. This remote interface will have a method, which can be called especially for returning the last location logged. Only primitive types and objects that implement the Parcelable
protocol can be used with remote service calls. This is because these calls cross process boundaries where memory can’t be shared. The AIDL compiler handles the details of crossing these boundaries when the rules are followed. The Location
object implements the Parcelable
interface so it can be used.
Here is the AIDL file for this interface, IRemoteInterface
:
When using Eclipse, this AIDL file, IRemoteInterface.aidl
, can be added to the project under the appropriate package and the Android SDK plug-in does the rest. Now we must implement the code for the interface. Here is an example implementation of this interface:
The service code already stored off the last location received as a member variable, so we can simply return that value. With the interface implemented, it needs to be returned from the onBind()
method of the service:
If multiple interfaces are implemented, the Intent
passed in can be checked within the onBind()
method to determine what action is to be taken and which interface should be returned. In this example, though, we have only one interface and don’t expect any other information within the Intent
, so we simply return the interface.
We also add the class name of the binder interface to the list of actions supported by the intent filter for the service within the AndroidManifest.xml
file. Doing this isn’t required but is a useful convention to follow and allows the class name to be used. The following block is added to the service tag definition:
<action android:name =
"com.androidbook.services.IRemoteInterface" />
The service can now be used through this interface. This is done by implementing a ServiceConnection
object and calling the bindService()
method. When finished, the unbindService()
method must be called so the system know that the application is done using the service. The connection remains even if the reference to the interface is gone.
Here is an implementation of a ServiceConnection
object’s two main methods, onServiceConnection()
and onServiceDisconnected()
:
When the onServiceConnected()
method is called, an IRemoteInterface
instance is retrieved that can be used to make calls to the interface we previously defined. A call to the remote interface would look like any call to an interface now:
Location loc = mRemoteInterface.getLastLocation();
Remember that remote interface calls operate across process boundaries and are completed synchronously. If a call takes a while to complete, it should be placed within a separate thread, as any lengthy call would be.
To use this interface from another application, the AIDL file should be placed within the project and appropriate package. The call to onBind()
triggers a call to the onServiceConnected()
after the call to the service’s onCreate()
method. Remember, the onStart()
method is not called in this case.
bindService(new Intent(IRemoteInterface.class.getName()),
this, Context.BIND_AUTO_CREATE);
In this case, the Activity
we call from also implements the ServiceConnection
interface. This code also demonstrates why it is a useful convention to use the class name as an intent filter. Because we have both intent filters and we don’t check the action on the call to the onBind()
method, we can also use the other intent filter, but the code here is clearer.
When done with the interface, a call to unbindService()
disconnects the interface. However, a callback to the onServiceDisconnected()
method does not mean that the service is no longer bound; the binding is still active at that point, just not the connection.
In the example so far, we have been lucky in that the Location
class implements the Parcelable
interface. What if a new object needs to be passed through a remote interface?
Let’s take the following class, GPXPoint
, as an example:
The GPXPoint
class defines a location point that is similar to a GeoPoint
but also includes the time the location was recorded and the elevation. This data is commonly found in the popular GPX file format. On its own, this is not a basic format that the system recognizes to pass through a remote interface. However, if the class implements the Parcelable
interface and we then create an AIDL file from it, the object can be used in a remote interface.
To fully support the Parcelable
type, we need to implement a few methods and a Parcelable.Creator<GPXPoint>
. The following is the same class now modified to be a Parcelable
class:
The writeToParcel()
method is required and flattens the object in a particular order using supported primitive types within a Parcel
. When the class is created from a Parcel
, the Creator
is called, which, in turn, calls the private constructor. For readability, we also created a readFromParcel()
method that reverses the flattening, reading the primitives in the same order that they were written and creating a new Date
object.
Now the AIDL file for this class must be created. It should be placed next to the Java file and named GPXPoint.aidl
to match. The contents should be made to look like the following:
Now the GPXPoint
class can be used in remote interfaces. This is done in the same way as any other native type or Parcelable
object. The IRemoteInterface.aidl
file can then be modified to look like the following:
Additionally, we can provide an implementation for this method within the interface, as follows:
As can be seen, nothing particularly special needs to happen. Just by making the object Parcelable
, it can now be used for this purpose.
The Android SDK provides the Service
mechanism that can be used to implement background tasks and to share functionality across multiple applications. By creating an interface through the use of AIDL, a Service
can expose functionality to other applications without having to distribute libraries or packages. Creating objects with the Parcelable
interface allows developers to extend the data that can be passed across process boundaries, as well.
Care should be taken when creating a background service. Poorly designed background services might have substantial negative impact on handset performance and battery life. In addition to standard testing, a Service
implementation should be tested with respect to these issues.
Prudent creation of a Service
, though, can dramatically enhance the appeal of an application or service you might provide. Service
creation is a powerful tool provided by the Android SDK for designing applications simply not possible on other mobile platforms.
18.219.215.178