10 things you didn’t know about Android’s Service component

You won’t believe n.3 is true!

I’m terribly sorry for the clickbait-y title, I just couldn’t resist.

Over the years I had more than a few conversations with other fellow Android developers, and one constant topic that kept popping up is how much the Service component is misunderstood, across both beginners and experts. This list is the result of those discussions: it has been wandering in the back of my mind for quite some time, and I finally decided to give it physical (digital?) shape here.

This is not meant to explain in detail how Service works, the official documentation (here, here and here) does a surprisingly good job at that; instead, it’s a collection of overlooked/misunderstood/forgotten concepts that are essential to grasp in order to master the subject.

1. A Service is not a “better AsyncTask”

It’s not made to perform generic asynchronous/background operations: its purpose is to execute logic even when no Activity is visible; think of it as an invisible Activity.

Keep in mind that each Service is a special component that has a cost, not just for your app, but for the entire system.

2. A Service by default runs on the main thread, in the same process of your application

You can optionally run it in a different process, but you should avoid it unless it’s absolutely necessary and you understand all the costs associated with doing so.

3. There’s no magic in IntentService

It works by creating a HandlerThread on which it queues the work to be done, a technique you can easily use outside of a Service.

It’s a simple class, long only 164 lines (74 without comments), go check it out!

4. There can only be one instance of a Service at a time

No matter how you create it, there can only ever be one instance at a time, even when external applications/processes interact with it.

5. A Service can be killed pretty easily

Don’t think that memory pressure is an “exceptional” condition: design your Service to gracefully handle restarts by the system, it’s a natural part of its lifecycle.

You can flag it as foreground to make it harder to kill, but do it only if absolutely necessary.

Note that when code is running inside onCreate(), onStartCommand(), or onDestroy(), it’s treated as if it was in foreground even if it’s not.

See here to understand how likely it is for your process to be killed.

6. A Service can be “started”, “bound”, or both at the same time

Stopping it explicitly won’t terminate it as long as there are bound components, and unbinding all components won’t terminate it until it’s explicitly stopped (if it was ever started). Also note that no matter how many times you call startService(), a single call to stopService() or stopSelf() will stop it.

See this useful chart:

Lifecycle of started and bound modes

7. START_FLAG_REDELIVERY avoids losing input data

If you pass data while starting a Service, returning the flag START_FLAG_REDELIVERY in onStartCommand() is useful to avoid losing it if the Service is killed while processing it.

8. Foreground notifications can be partially hidden

A foreground Service has to show a persistent notification, but you can give it a priority value of PRIORITY_MIN to hide it from the status bar (it will still be visible in the notification shade).

9. A Service can start an Activity

Like every Context that is not an Activity, you can start an Activity from a Service if you add the FLAG_ACTIVITY_NEW_TASK flag.

You can also show Toast and Status Bar notifications.

10. You are allowed to use the Single Responsibility Principle

Incredibly enough, you should not bundle your business logic in the Service class, but in a separate class. That way, among many other benefits, you are free to run it anywhere else, if needed. Amazing!

Now keep this list in mind, spread the word, and help stop Service abuse!