Do you really need a Service in Android?

Contents

Introduction

I've been developing Android apps for quite a long time and one question has always been eating me: when and why do I need to use android.app.Service? The question might be simple at first glance but is more complicated after detailed examine.

Problem and possible solutions

Let's say that we want to create an app with some heavy background work, e.g. messenger or image gallery. For messenger background work is to communicate with remote service (send and receive messages), for image gallery - to download images (as we know from ICS this work must not be done on main app thread as it will cause crash). How can we implement this? Obvious solutions are:
  1. Using android.os.AsyncTask
  2. Using android.app.Service
  3. Writing own solution - manual thread management (Threads, ExecutorServices, etc)
Of course the experienced developer immediately says: AsyncTask is not designed for long operations. And he is correct, here is the part of documentation for AsyncTask (source):

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

OK, first solution is bad, what else do we have:
  1. Using android.os.AsyncTask
  2. Using android.app.Service
  3. Writing own solution - manual thread management (Threads, ExecutorServices, etc)
Let's read documentation for android.app.Service (source):

A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use

That's what we were looking for - long running operations in background. Cool, let's code:
public final class DownloadImageService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        // initialization logic
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);

        // start logic
    }
}
                            
Let's say we need to load some data (e.g. images from remote server):

    @Nonnull
    private static final String EXTRA_URL = "url";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int result = super.onStartCommand(intent, flags, startId);
        downloadImage(intent.getStringExtra(EXTRA_URL));
        return result;
    }

    private void downloadImage(@Nonnull String url) {
        // downloading
    }
                            
Compile, package, start - bump - android.os.NetworkOnMainThreadException. WTF? It turns out that Android service is working on the main application thread (source):

Note that services, like other application objects, run in the main thread of their hosting process.

OK, then we need to create a manual background thread management class and use it inside the service... Stop. But why do we need service after all? It's much easier to communicate with local class, we can use callbacks/listeners with local class and we cannot use it all with service. Service binding takes some time and if we want to use local service we need to wait until it will be bound which makes code less clean. Service only accepts input which is parcelable (as we need to put in into Intent (for remote service) or use AIDL (for local service)). And we've come to a conclusion that only one right way of implementing background task is to create own solution (as Android doesn't provide it out of the box):
  1. Using android.os.AsyncTask
  2. Using android.app.Service
  3. Writing own solution - manual thread management (Threads, ExecutorServices, etc)
Summarizing everything above:
  1. Android Service doesn't help you to create background task
  2. Most probably you don't need Service at all
  3. Sometimes documentation is misleading and you should not always rely on it

Solution

I can suggest next solution (included in Java Common Library and Android Common Library): TaskService interface and it's implementation. Utility class Tasks hides the link between implementation and interface and provides methods for creation of TaskService. I hope that these classes are self-explanatory but if you have any questions - ask them in the comments.

When to use Service?

The last question to be answered - when do we really need Service?
I can name only 2 possible reasons to use it:
  1. When we need to say Android that our application is doing some important things in background and should not be killed. To achieve this we can run Service as foreground via startForeground method
  2. When we need to provide some functionality of our app to the other app. This can be done via exporting service in manifest file
That's all. If you don't need to achieve one of these goals - you don't need Service at all.

The end

Hope this article helped someone to understand better how to implement background task management and reviles the common misunderstanding of Android services. If you have any questions you can ask them in the comments on Reddit or on Google+.

Code responsible and may the force be with you.