Services

What is a service?

service is an app component that performs long-running operations, usually in the background. Unlike an Activity, a service doesn't provide a user interface (UI). Services are defined by the Service class or one of its subclasses.

A service can be startedbound, or both:

  • started service is a service that an app component starts by calling startService().

    Use started services for tasks that run in the background to perform long-running operations. Also use started services for tasks that perform work for remote processes.

  • bound service is a service that an app component binds to itself by calling bindService().

    Use bound services for tasks that another app component interacts with to perform interprocess communication (IPC). For example, a bound service might handle network transactions, play music, or interact with a database.

A service runs in the main thread of its hosting process—the service doesn't create its own thread and doesn't run in a separate process unless you specify that it should.

If our service is going to do any CPU-intensive work or blocking operations (such as MP3 playback or networking), create a new thread within the service to do that work. By using a separate thread, we reduce the risk of the user seeing "application not responding" (ANR) errors, and the app's main thread can remain dedicated to user interaction with our activities.

To implement any kind of service in your app, do the following steps:

  1. Declare the service in the manifest.
  2. Extend a Service class such as IntentService and create implementation code, as described in Started services and Bound services, below.
  3. Manage the service lifecycle.

Declaring services in the manifest

To declare a service, add a <service> element as a child of the <application> element. For example:

<manifest ... >
  ...
  <application ... >
      <service android:name="ExampleService"
               android:exported="false" />
      ...
  </application>
</manifest>

To block access to a service from other apps, declare the service as private. To do this, set the android:exported attribute to false. This stops other apps from starting your service, even when they use an explicit intent.

Started services

How a service starts:

  1. An app component such as an Activity calls startService() and passes in an Intent. The Intent specifies the service and includes any data for the service to use.
  2. The system calls the service's onCreate() method and any other appropriate callbacks on the main thread. It's up to the service to implement these callbacks with the appropriate behavior, such as creating a secondary thread in which to work.
  3. The system calls the service's onStartCommand() method, passing in the Intent supplied by the client in step 1. (The client in this context is the app component that calls the service.)

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. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself by calling stopSelf(), or another component can stop it by calling stopService().

For instance, suppose an Activity needs to save data to an online database. The Activity starts a companion service by passing an Intent to startService(). The service receives the intent in onStartCommand(), connects to the internet, and performs the database transaction. When the transaction is done, the service uses stopSelf() to stop itself and is destroyed. (This is an example of a service you want to run in a worker thread instead of the main thread.)

IntentService

Most started services don't need to handle multiple requests simultaneously, and if they did, it could be a complex and error-prone multi-threading scenario. For these reasons, the IntentService class is a useful subclass of Service on which to base your service:

  • IntentService automatically provides a worker thread to handle your Intent.
  • IntentService handles some of the boilerplate code that regular services need (such as starting and stopping the service).
  • IntentService can create a work queue that passes one intent at a time to your onHandleIntent() implementation, so you don't have to worry about multi-threading.
Note:IntentService is subject to the new restrictions on background services in Android 8.0 (API 26). For this reason, Android Support Library 26.0.0 introduces a new JobIntentService class, which provides the same functionality as IntentService but uses jobs instead of services when running on Android 8.0 or higher.

To implement IntentService:

  1. Provide a small constructor for the service.
  2. Create an implementation of onHandleIntent() to do the work that the client provides.

Here's an example implementation of IntentService:

public class HelloIntentService extends IntentService {
  /**
   * A constructor is required, and must call the 
   * super IntentService(String) constructor with a name 
   * for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default 
   * worker thread with the intent that started the service. 
   * When this method returns, IntentService stops the service, 
   * as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
      try {
          Thread.sleep(5000);
      } catch (InterruptedException e) {
          // Restore interrupt status.
          Thread.currentThread().interrupt();
      }
  }
}

Bound services

A service is "bound" when an app 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, and get results, sometimes using interprocess communication (IPC) to send and receive information across processes. A bound service runs only as long as another app component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.

Implementing a bound service

To implement a bound service, define the interface that specifies how a client can communicate with the service. This interface, which your service returns from the onBind() callback method, must be an implementation of IBinder.

To retrieve the IBinder interface, a client app component calls bindService(). Once the client receives the IBinder, the client interacts with the service through that interface.

There are multiple ways to implement a bound service, and the implementation is more complicated than a started service.

Binding to a service

To bind to a service that is declared in the manifest and implemented by an app component, use bindService() with an explicit Intent.

Caution: Do not use an implicit intent to bind to a service. Doing so is a security hazard, because you can't be certain what service will respond to your intent, and the user can't see which service starts. Beginning with Android 5.0 (API level 21), the system throws an exception if you call bindService() with an implicit Intent .

Service lifecycle

A service has no UI, services can continue to run in the background with no way for the user to know, even if the user switches to another app. This situation can potentially consume resources and drain the device battery.

Similar to an Activity, a service has lifecycle callback methods that we can implement to monitor changes in the service's state and perform work at the appropriate times. The following skeleton service implementation demonstrates each of the lifecycle methods:

public class ExampleService extends Service {
    // indicates how to behave if the service is killed.
    int mStartMode;  

    // interface for clients that bind.
    IBinder mBinder;

    // indicates whether onRebind should be used   
    boolean mAllowRebind; 

    @Override
    public void onCreate() {
        // The service is being created.
    }

    @Override
    public int onStartCommand(Intent intent, 
        int flags, int startId) {
        // The service is starting, due to a call to startService().
        return mStartMode;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService().
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }

    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Lifecycle of started services vs. bound services

A bound service exists only to serve the app component that's bound to it, so when no more components are bound to the service, the system destroys it. Bound services don't need to be explicitly stopped the way started services do (using stopService() or stopSelf()).

The diagram below shows a comparison between the started and bound service lifecycles. Started and Bound Service Lifecycle, noborder

Scheduled services

For API level 21 and higher, you can launch services using the JobScheduler API, and with the background service restrictions in API 26 this may be a better alternative to services altogether. To use JobScheduler, you need to register jobs and specify their requirements for network and timing. The system schedules jobs for execution at appropriate times.

Comments

Popular posts from this blog

Android - Using KeyStore to encrypt and decrypt the data

Stack and Queue

Java Reflection API