Services

Table of Contents

  1. Introduction
  2. Started service as IntentService
    1. Sending local broadcast message
    2. Listening for a local broadcast message
  3. Started service as Service
  4. Bound service with Binder class
  5. Bound service with Messanger
  6. Bound service with AIDL

Introduction

A service is an application component that can perform long-running operations in the background and does not provide a user interface. A service can essentially take two forms:

Started
A service is “started” when an application component (such as an activity) starts it by calling startService(). Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller.
Bound
A service is “bound” when an application component binds to it by calling bindService(). A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.

Any service can work both ways—it can be started (to run indefinitely) and also allow binding. It’s a matter of whether we implement a necessary callback methods onStartCommand() to allow components to start it and onBind() to allow binding.

Simple in theory, Android services are quite complicated in practise mainly because service can be realized in many different ways.

Service as an IntentService
IntentService is a simple type of service that can be used to handle asynchronous work off the main thread by way of Intent requests. Each intent is added to the IntentService’s queue and handled sequentially. An operation running on an IntentService can’t be interrupted. IntentService is one of the simplest ways to offload the UI thread of application. To do this we have to define service (as IntentService), package up an intent with the appropriate data we want to send for processing, and start the service. To send data back to the application we broadcast the result as an Intent object, and use a broadcast receiver to catch the result within the app.
Service as a Service
Using IntentService makes an implementation of a service very simple. If, however, we require to perform multi-threading (instead of simply processing start requests through a work queue), then we can extend the base Service class to handle each intent.
Bound service
When creating a service that provides binding, we must provide an IBinder that provides the programming interface that clients can use to interact with the service. There are three ways we can define the interface:

  1. Extending the Binder class. If the service is private to the application and runs in the same process as the client (which is very common), we should create an interface by extending the Binder class and returning an instance of it from onBind() method. The client receives the Binder and can use it to directly access public methods available in either the Binder implementation or even theService. This is the preferred technique when the service is merely a background worker for an application. The only reason we would not create an interface this way is because the service is used by other applications or across separate processes.
  2. If we need the interface to work across different processes, we can create an interface for the service with a messenger (Messenger). In this manner, the service defines a handler (Handler) that responds to different types of message objects. This handler is the basis for a messenger that can then share an IBinder with the client, allowing the client to send commands to the service using Message objects. Additionally, the client can define a Messenger of its own so the service can send messages back. This is the simplest way to perform interprocess communication, because the messenger queues all requests into a single thread so that we don’t have to design the service to be thread-safe.
  3. Using AIDL (Android Interface Definition Language) performs all the work to decompose objects into primitives that the operating system can understand and marshall them across processes to perform IPC. The previous technique, using a messenger, is actually based on AIDL as its underlying structure. As mentioned above, the messenger creates a queue of all the client requests in a single thread, so the service receives requests one at a time. If, however, we want the service to handle multiple requests simultaneously, then we can use AIDL directly. In this case, the service must be capable of multi-threading and be built thread-safe. To use AIDL directly, we must create an .aidl file that defines the programming interface. The Android SDK tools use this file to generate an abstract class that implements the interface and handles IPC, which we can then extend within the service.

Service as an IntentService

To use IntentService we have to implement only one method: onHandleIntent(). This method is where processing occurs. Any data necessary for processing request can be packaged in the intent extras.

Do not forget to declare service in manifest. Add a <service> element as a child of the <application> element. For example:

Now we can start the service from application activity and delegate the processing to the service.

That is all. The question is how to return some results to application from IntentService? Thers is no any dedicated mechanism for this. Standard approach is to broadcast the result as an Intent object, and use a broadcast receiver to catch the result within the app.

However we can consider to use LocalBroadcastManager. This has a number of advantages over sending global broadcasts with standard sendBroadcast(Intent):

  • We know that the data we are broadcasting won’t leave our app, so don’t need to worry about leaking private data.
  • It is not possible for other applications to send these broadcasts to our app, so we don’t need to worry about having security holes they can exploit.
  • It is more efficient than sending a global broadcast through the system.

The LocalBroadcastManager used to send local broadcast messages is not part of the “normal” Android API.  It is part of the Android Support package.  In order to add the Android Support package to your Android application environment, open the Android SDK Manager, find the “Extras” folder and select Android Support package for installation. Once the Support package is part of your environment, make sure the package is available to your project.

Sending local broadcast message

Listening for a local broadcast message

Service as a Service

To use Service we have to override some callback methods that handle key aspects of the service lifecycle and provide a mechanism for components to bind to the service, if appropriate. The most important callback methods we should override are:

onStartCommand()
The system calls this method when another component, such as an activity, requests that the service be started, by callingstartService(). Once this method executes, the service is started and can run in the background indefinitely. If we implement this, it is our responsibility to stop the service when its work is done, by calling stopSelf() or stopService(). In case we want only to provide binding, we don’t need to implement this method.
onBind()
The system calls this method when another component wants to bind with the service, by calling bindService(). In our implementation of this method, we must provide an interface that clients use to communicate with the service, by returning an IBinder. We must always implement this method, but if we don’t want to allow binding, then we should return null.
onCreate()
The system calls this method when the service is first created, to perform one-time setup procedures, before it calls either onStartCommand() or onBind(). If the service is already running, this method is not called.
onDestroy()
The system calls this method when the service is no longer used and is being destroyed. The service should implement this to clean up any resources such as threads, registered listeners, receivers, etc. This is the last call the service receives.

 

Notice that the onStartCommand() returns an integer startMode. The integer is a value that describes how the system should continue the service in the event that the system kills it. The return value fromonStartCommand() must be one of the following constants:

START_NOT_STICKY
If the system kills the service after onStartCommand() returns, do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.
START_STICKY
If the system kills the service after onStartCommand() returns, recreate the service and callonStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
START_REDELIVER_INTENT
If the system kills the service after onStartCommand() returns, recreate the service and callonStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.

Finally do not forget to declare service in manifest.

Now we can start the service from application activity and delegate the processing to the service as we did it for IntenService

The startService() method returns immediately. If the service is not already running, the system first calls onCreate(), then calls onStartCommand().

If the service does not also provide binding, the intent delivered with startService() is the only mode of communication between the application component and the service. However, if we want the service to send a result back, then we can use again approach described in IntentService.

Multiple requests to start the service result in multiple corresponding calls to the service’s onStartCommand(). However, only one request to stop the service (with stopSelf() or stopService()) is required to stop it.

Bound service with Binder class

If the service is used only by the local application and does not need to work across processes, then we can implement Binder class which provides the client direct access to public methods in the service. This works only if the client and service are in the same application and process.

To use this approach we have to:

  1. In the service, create an instance of Binder that either:
    • contains public methods that the client can call
    • returns the current Service instance, which has public methods the client can call
    • or, returns an instance of another class hosted by the service with public methods the client can call
  2. Return this instance of Binder from the onBind() callback method.
  3. In the client, receive the Binder from the onServiceConnected() callback method and make calls to the bound service using the methods provided.

The LocalBinder provides the getService() method for clients to retrieve the current instance of ServiceWithBind. This allows clients to call public methods in the service. The code below shows how the client binds to the service using an implementation of ServiceConnection and the onServiceConnected() callback.

Clients can bind to a service by calling bindService(). The system then calls the service’s onBind() method, which returns an IBinder for interacting with the service. The binding is asynchronous. bindService() returns immediately and does not return the IBinder to the client. To receive the IBinder, the client must create an instance of ServiceConnection and pass it to bindService(). The ServiceConnection includes a callback method that the system calls to deliver the IBinder.

So, to bind to a service from your client, we have to:

  1. Implement ServiceConnection and override two callback methods:
    onServiceConnected()
    The system calls this to deliver the IBinder returned by the service’s onBind() method.
    onServiceDisconnected()
    The system calls this when the connection to the service is unexpectedly lost, such as when the service has crashed or has been killed. This is not called when the client unbinds.
  2. Call bindService(), passing the ServiceConnection implementation.
  3. When the system calls onServiceConnected() callback method, we can begin making calls to the service, using the methods defined by the interface.
  4. To disconnect from the service, call unbindService().When the client is destroyed, it will unbind from the service, but we should always unbind when we are done interacting with the service or when the activity pauses so that the service can shutdown while its not being used.

Only activities, services, and content providers can bind to a service. It is not possible to do this from a broadcast receiver.

Bound service with Messanger

In case we want the service to communicate with remote processes, then we can use a Messenger to provide the interface for the service. This technique allows to perform interprocess communication (IPC) without the need to use AIDL.

To use a Messenger we have to:

  • Implements in the service a Handler that receives a callback for each call from a client.
  • The Handler is used to create a Messenger object (which is a reference to the Handler).
  • The Messenger creates an IBinder that the service returns to clients from onBind().
  • Clients use the IBinder to instantiate the Messenger (that references the service’s Handler), which the client uses to send Message objects to the service.
  • The service receives each Message in its Handler‘s handleMessage() method.

From the above we can see that, there are no “methods” for the client to call on the service. Instead, the client delivers “messages” (Message objects) that the service receives in its Handler.

Let’s take a look into the following example of server.

All that a client needs to do is create a Messenger based on the IBinder returned by the service and send a message using send().

Notice that with Messenger we create only one direction connection (in this case from the client to the service). If we want the service to respond, then we need to also create a Messenger in the client. Then when the client receives the onServiceConnected() callback, it sends a Message to the service that includes the client’s Messenger in the replyTo parameter of the send() method.

Bound service with AIDL

This is the most advanced (complicated) tehinque. Services with AIDL are quite well described in http://developer.android.com/guide/components/aidl.html

Leave a Reply

Your email address will not be published. Required fields are marked *