Paul Brower bio photo

Paul Brower

Android development for fun and profit.

Email Twitter Github Stackoverflow Codementor

There are quite a few misnomers when it comes to the Android Service class. Let’s clear some of them up.

What most people think of when they think of services

  • run independently of user interaction
  • don’t commonly have a GUI or can run without one
  • use a client for interaction (web browser, db client, sockets)
  • independent threading
  • long running

This is the common understanding of a UNIX service

How Android defines a service

  • long running
  • does not provide a user interface
  • independent of application lifecycle
  • can be “bound” to

You’ll notice that threading it not part of this. This is the source of great confusion for many developers. A service is in the “background” in that it does not interact with the user but it still runs on the main thread. The same thread that handles user interaction, View hierarchy updates and activity lifecycle events. When you start a service it is blocking user interaction.

This is rather inconvenient, so the SDK includes a class that puts the threading on a separate thread.

Introducing IntentService

http://developer.android.com/reference/android/app/IntentService.html

IntentService is a Service that has baked-in asynchronous handling. Every service (IntentService or regular Services) is started by a call to

Context.startService(android.content.Intent)

a regular Service would then handle this Intent starting with onStartCommand using the main thread. If you were doing some long running operation (reading from a db, reading from the filesystem, long computation) you would need to create your own threading model, whether it be with an ExecutorService or using a Handler

IntentService does this work for you. It sends intents to a worker thread that calls a method you will be overriding called onHandleIntent(android.content.Intent). All code in onHandleIntent will be done on a single worker thread.

A simple IntentService could look like this:

public class SimpleLoggingIntentService extends IntentService {

  /**
  * A constructor is the only other method you need to override.  It's
  * important to call super with a string unique to this class as it will
  * be used to name the worker thread.
  */
  public SimpleLoggingIntentService() {
    super("SimpleLoggingIntentService");
  }

  /**
  * This is called on the worker thread for each intent received through
  * startService
  */
  @Override
  protected void onHandleIntent(Intent intent) {

    Log.d("SimpleLoggingIntentService","starting onHandleIntent")
    // Do work here (downloads, DB access, CPU intensive operations).  We'll
    // simulate with a sleep call.
    try {
        Thread.sleep(5000);                 //sleep 5s.
    } catch(InterruptedException ex) {
        Log.e("SimpleLoggingIntentService", ex.getMessage(), ex);
    }
    Log.d("SimpleLoggingIntentService","finishing onHandleIntent")

  }
}

This service passes and Intents sent to startService to onHandleIntent on a worker thread which will handle them in serial according to FIFO (first in first out).

Once all the intents have been handled, the IntentService is stopped.

When to use a Service instead of an IntentService

For most situations, an IntentService will handle all that you need. The threading model works well and is usually enough for most applications. However, there are times when a traditional Service is more appropriate. Let’s look at a multithreading example.

Let’s say we want to download lots of images to the app folder on the device. These images are small, maybe profile images at 64x64, but there are a lot of them. We could handle this in an IntentService. We could give an IntentService an Intent for each icon we want to download and it will execute them in serial. But that could be very slow. If we download them in parallel it will be much faster. Let’s create our own service that does this:

public class MultiThreadedService extends Service {

  public static final String _URL = "url";
  private ExecutorService executorService;
  // counter for number of files we have downloaded
  private int downloadsStarted;
  // counter for number of files done
  private int downloadsFinished;
  private Handler handler;

  @Override
  public void onCreate() {
    super.onCreate();
    // let's create a thread pool with five threads
    executorService = Executors.newFixedThreadPool(5);
    downloadsStarted = 0;
    downloadsFinished = 0;
    handler = new Handler();
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {

    // lets say that we are sending the url in the intent
    String url = intent.getStringExtra(_URL);

    // create a runnable for the ExecutorService
    DownloadRunnable task = new DownloadRunnable(getApplicationContext(), url, downloadsStarted++);

    // submit it to the ExecutorService, this will be put on the queue and run using a thread
    // from the ExecutorService pool
    executorService.submit(task);

    // tells the OS to restart if we get killed after returning
    return START_STICKY;
  }

  /**
  * we don't care about binding right now, returning null is the way to do that
  *
  * @param intent
  * @return
  */
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }

  /**
  * to be called by the dl task.  if we have exhausted our list of dl's let's shutdown.
  */
  private void finished() {
    if (downloadsFinished == downloadsStarted) {
      handler.post(new Runnable() {
        @Override
        public void run() {
          Log.d("MultiThreadedService", "downloaded " + downloadsFinished + " images, shutting down.");
          stopSelf();
        }
      });

    }
  }

  /**
  * dl's the image in question to <index>.jpg
  */
  private class DownloadRunnable implements Runnable {

    private static final int BUFFER_SIZE = 4096;

    private Context context;
    private String url_str;
    private int index;

    public DownloadRunnable(Context context, String url, int index) {
      this.context = context;
      this.url_str = url;
      this.index = index;
    }

    @Override
    public void run() {
      URL url = null;
      HttpURLConnection httpConn = null;
      try {
        url = new URL(url_str);

        httpConn = (HttpURLConnection) url.openConnection();

        // opens input stream from the HTTP connection
        InputStream inputStream = httpConn.getInputStream();
        String saveFilePath = context.getFilesDir() + File.separator + index + ".jpg";

        // opens an output stream to save into file
        FileOutputStream outputStream = new FileOutputStream(saveFilePath);

        int bytesRead = -1;
        byte[] buffer = new byte[BUFFER_SIZE];
        while ((bytesRead = inputStream.read(buffer)) != -1) {
          outputStream.write(buffer, 0, bytesRead);
        }

        outputStream.close();
        inputStream.close();

        Log.d("MultiThreadedService", "File downloaded to " + saveFilePath);

      } catch (Exception e) {
        Log.e("MultiThreadedService", e.getMessage(), e);
      } finally {
        if (httpConn != null) {
          httpConn.disconnect();
        }

      }

      downloadsFinished++;
      finished();

    }
  }
}

This will download every image given to it using the ExecutorService but will return immediately from onStartCommand. Once all the images are downloaded the service will shut itself down. This runs independently of user interaction and offloads the slow download execution to worker threads. If you want to see all the code go to https://github.com/browep/ServicesTutorial.

Further Reading

There is a lot more to Services and anyone making a decently sized app should know when to use them and when not to. See: http://developer.android.com/guide/components/services.html for further reading.