Skip to content

Instantly share code, notes, and snippets.

@HectorRicardo
Last active April 19, 2022 13:22
Show Gist options
  • Save HectorRicardo/9ed7393da750ffc192bfb74d6ef38bae to your computer and use it in GitHub Desktop.
Save HectorRicardo/9ed7393da750ffc192bfb74d6ef38bae to your computer and use it in GitHub Desktop.

Android Services

A service is an application component that performs work in the background, without a visible user-interface. For example, a service could be an audio player playing music while the user is in another app or has the device locked. While the current song is playing, the service might also be retrieving the next song from the network.

A service runs in the main thread of its hosting app process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. You should run any blocking operations on a separate thread within the service to avoid Application Not Responding (ANR) errors.

As with other application components, the service class must be a top-level class (i.e. not an inner class of another class) and be declared in the manifest. (In the manifest) you can declare such class as:

  • private: only application components within your app can use the service
  • public: application components from outside your app can use the service as well

Services can be non-mutually-exclusively qualified as:

  • Startable: Qualified as such if the service implements the onStartCommand method. When the service starts, it performs long-running operations in the background that continue running for some time, even after the user switches to another application or locks the device.
    • A service enters the STARTED state when another application component, such as an activity or an already-existing service, calls startService().
      • You pass an Intent referencing the service class to the startService method
      • If no instance of the service class exists yet, one is instantiated and its onCreate method is called. Otherwise, the existing instance is used.
      • The service's onStartCommand() method then is executed.
    • After the service is started, it runs until it is stopped.
      • To stop the service, you call stopService from a different application component, or stopSelf from within the service.
    • The user might or might not need to be actively aware of the execution of this service. If he needs to be aware (such as in the music player example), then we classify this service as a foreground service (aka we "put the service in the foreground" or "make the service a foreground service").
      • In this context, "foreground" doesn't mean "in the current visible screen". Rather, it's a classification that lets Android know that this is a special service that has some important considerations (mentioned a few bullets below).
      • Only the service itself can move itself to the forground. After being started as a non-foreground service, the service calls this.startForeground() to do so (this = the service).
        • The application component that starts the service cannot move it to the foreground, or start it directly in the foreground. However, it can danger-validate1 that the service actually puts itself into the foreground shortly after being started. It does this by calling Context.startForegroundService() instead of startService(). This will start the service as usual (as if it were a non-foreground service), but it will also set a 5-second timer. The started service will need to call this.startForeground() before the timer expires. Otherwise, the system will stop the service and declare the app to be non-responsive.
      • Foreground services should display a notification so that users are aware that the service is running.
        • This notification cannot be dismissed unless the service is stopped or removed from the foreground.
      • You can bring a service to the foreground and remove it from the foreground (aka, send it to the "background") as often as you wish. However, every time you add it to the foreground, you need to associate it to a notification.
  • Boundable: Technical definition: A service that has a non-nullable onBind() method. (will explain this method a few bullets below).
    • Application components can bind (connect) to a boundable service and communicate with it.
      • When this happens, we say that the service is BOUND, and we call the binding application components "clients". (Sometimes, by sloppy language, Android docs uses the word "bound" as a synonym of "boundable". but I won't follow this tendency).
    • The service offers a client-server interface (the service being the server) that allows the client to interact with it.
      • The service handles requests from the client, such as network transactions, playing music, performing file I/O.
    • A client binds to the service by calling bindService(), passing it an Intent referencing the service class it wants to bind to.
      • If no instance of the service class exists yet, one is instantiated and its onCreate method is called. Otherwise, the existing instance is used.
      • The service's onBind method is then called.
        • This method returns the client-server interface (as an instance of IBinder) explained above.
    • The client doesn't need to be from the same app/process as the service.
    • To unbind a client from the service, you call

These qualities are totally non-mutually exclusive: a service can simultaneously be startable/started, boundable/bound, and running in the foreground.

The lifetime of the service depends on these qualities:

  • A started service remains alive as long as it isn't stopped (even if it's unboundable or no clients are bound to it).
  • A bound service remains alive as long as there is at least one client bound to it (even if it's not startable or hasn't started).

Once both of these conditions stop being true, the service is destroyed. The onDestroy method is called, which performs cleanup of any resources such as threads, registered listeners, and receivers. This is the last call the service receives.

The following flowchart demonstrates how the lifecycle of a service is managed. The variable counter tracks the number of bound clients.

image

From the diagram, observe that you create a service by either starting it or binding to it. There's no way to directly create a service without doing one of these. You have to start the service or bind to it. The service will be automatically created for you if it doesn't exist yet.

Footnotes

  1. Danger-validate is a term that I coined. It means to perform validations and throw an exception/cause a system failure if such validations fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment