Services

A service is an application component that is designed

  • to perform long-running operations in the background such as playing music
  • inter process communication, i.e. IPC. This article does not cover this.

A service doesn't has any UI. But service can be bounded by a component to perform interactivity.

A service runs in the main thread. You should run any blocking operations on a separate thread within the service to avoid Application Not Responding (ANR) errors.

The service can run in the background even after the application is destroyed.

register service

To use service you need declare it in the AndroidManifest.xml file. The main properties are

  • name - a class name of service
  • description - description of service for user
  • exported - true means that any application can use the service, false means that the service is only available to your app
Declare service in manifest
<manifest ... >
  ...
  <application ... >
      <service android:name=".MyService" 
       android:description="@string/my_service_description"
       android:exported="false"
      />
      ...
  </application>
</manifest>

started service

A started service service that is started from the startService() method of context. There are three ways to stop such service:

  • stopSelf() is used to always stop the current service.
  • stopSelf(startId) allows to stop the current service if startId was last command.
  • stopService(intent) method of context allows to stop service from outside.
Started service template
class MyService : Service() {

    // prepare the service for work
    override fun onCreate() { /* ... */ }
 
    // handle a request
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {   
        // extract data from intent and do job
        // for example intent action can be used as command id
        // ...
        return START_STICKY
    }

    // not used, so return null
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onDestroy() {/* */}
}

The onStartCommand method can return following values:

  • 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 call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent.
  • START_REDELIVER_INTENT - if the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service.
Started service example

bound service

A bound service is the server in a client-server interface. It allows components such as activities to bind to the service, send requests, receive responses, and perform interprocess communication (IPC). A bound service typically lives only while it serves another application component and does not run in the background indefinitely.

A client binds to a service by calling bindService() method of context. To unbind, use the unbindService() method.

If your service is used only by the local application and does not need to work across processes, then you can implement your own Binder class that provides your client direct access to public methods in the service.

Bound service example
class LocalService : Service() {
    // Binder given to clients
    private val binder = LocalBinder()

    // Random number generator
    private val mGenerator = Random()

    /** method for clients  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods
        fun getService(): LocalService = this@LocalService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }
}

To use bound service client must follows these steps:

  1. implement ServiceConnection interface for monitoring the state of an service, there are two methods:
    • onServiceConnected()
    • onServiceDisconnected() - called 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. This can be done in onStart() method of activity.
  3. when the system calls your onServiceConnected() callback method, you can begin making calls to the service, using the methods defined by the interface.
  4. call unbindService() method to disconnect from the service. This can be done in onStop() method of activity. If your client is still bound to a service when your app destroys the client, destruction causes the client to unbind. It is better practice to unbind the client as soon as it is done interacting with the service.
Bound service usage

foreground service

Each foreground service must show a status bar notification. That way, users are actively aware that your app is performing a task in the foreground and is consuming system resources. The notification cannot be dismissed unless the service is either stopped or removed from the foreground.

If notification has priority lower than PRIORITY_LOW, the system adds a message to the notification drawer, alerting the user to the app's use of a foreground service.

You should only use a foreground service when your app needs to perform a task that is noticeable by the user even when they're not directly interacting with the app. For example

  • a music player might show the current song that is being played
  • a fitness app might show the distance that the user has traveled during the current fitness session

From API 28 you must add permission to the AndroidManifest.xml.

Add permission example

A foreground service must provide notification and call startForeground() method when started.

A foreground service must call stopForeground() method when stopped.

Stopping/starting service

To start/stop a service an activity can use intent with different commands (as with started services).

Start/stop service from activity
// start 
val startIntent = Intent(this@MainActivity, ForegroundService::class.java)
startIntent.action = Constants.ACTION.STARTFOREGROUND_ACTION
// startService(startIntent)
startForegroundService(startIntent) // API 26 +

//stop
val stopIntent = Intent(this@MainActivity, ForegroundService::class.java)
stopIntent.action = Constants.ACTION.STOPFOREGROUND_ACTION
startService(stopIntent)
//start
Intent startIntent = new Intent(MainActivity.this, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
// startService(startIntent)
startForegroundService(startIntent) // API 26 +

//stop
Intent stopIntent = new Intent(MainActivity.this, ForegroundService.class);
stopIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
startService(stopIntent);