The Role of Services

“Only have a service running when it is actively delivering value to the user”.

I use the above expression a lot, as it is the shortest way I have found to express when using a service is appropriate. However, as with all short explanations, it is not necessarily intuitively obvious what I mean.

Primarily, a service exists as a flag to the operating system, indicating that you are actively delivering value to the user, and therefore Android should aim to terminate other processes — not yours — when it gets low on system RAM. Secondarily, a service provides a couple of APIs for IPC: the “command” pattern (Intent and startService()) and the “binding” pattern (bindService()).

While we also use those two communication patterns for in-process use, that is mostly in support of the role of flagging our work’s existence to the OS. We organize our code around those communication patterns so that the service is responsible for the “actively delivering value” task, and so when the service is no longer doing that, we can stop the service.

When we have a service running, therefore, we are indicating to Android that we think that the user thinks that our process is more important to the user than are other competing processes, and so the user would prefer that Android terminate those competing processes rather than ours. This affects multitasking, as the more service-laden processes are around, the more quickly that ordinary processes the user might have used recently will be terminated just to free up RAM. This becomes particularly obvious to the user when the user tries switching back to the app and something is lost due to the process termination (e.g., because the app is not handling onSaveInstanceState() well).

The catch is that too many developers don’t think in terms of the user. They think in terms of themselves, believing that what is important to the developer must be important to the user. Sometimes, what is important to the developer works at cross-purposes to what is important to the user. For example, the developer wants to go home rather than clean up some sloppy service code, while the user wants multitasking to work well. What the developer wants (less work in cleaning up the service usage) and what the user wants (fewer processes with running services) are at odds.

Hence: only have a service running when it is actively delivering value to the user.

Here, “value”, like beauty, lies in the eyes of the beholder. And the user thinks the user is the beholder, and so the user is the one defining what is, and is not, “value”. The user must feel that what your service is doing for them is worth the worse multitasking, where they have to start up “Angry Birds: The Battle of the Five Flocks” again from the beginning, rather than return to their in-progress game, because Android terminated the birds’ process when the user took a phone call, rather than terminate your service’s process.

“Actively delivering” means that the service is delivering value right now. Not “maybe in a few minutes”. Not “well, sometime, but we don’t know when”. Right now. And the recipient of that “value” must be the user — you, the developer, are not more important than the user.

So, let’s work through some scenarios:

  • Having a service around for playing back the media for your music player app is actively delivering value to the user. The user will notice if the music were to all of a sudden stop due to your process being terminated, and so indicating to the OS that you are delivering value to the user is reasonable. In fact, this is one of a handful of scenarios where using startForeground() is relevant.

  • Having a service around to try to keep your process around rather than have to reload data is not actively delivering value to the user. Your process will not live forever anyway, and so you should be fixing the data-load speed issue to handle those process startups. That, in turn, will alleviate your need to try to artificially keep the process around to avoid the data-load work. The value to the user is having the data be loaded, but the long-running service should not be required to deliver that value.

  • Having a service around to download a large file is actively delivering value to the user, assuming that they value the file. This is an ideal use case for an IntentService, as you need a background thread anyway for the network and disk I/O, and the service can automatically shut itself down when the download is complete. That allows you to have the running service only while you are actively delivering value to the user, while having the service go away once the value goes away.

  • Having a service around all the time to do work periodically is not delivering value to the user, in most cases. While the periodic work is value, watching the clock tick in between pulses of work is not delivering value. Instead, use AlarmManager, or the new “L” JobScheduler.

  • Having a service around because you wanted to have a central point of control in your app is not delivering value to the user. Having a central point of control is reasonable, but you do not need it to be a service. For this role, an ordinary singleton is lighter weight, in that a service is a singleton that happens to have broader impacts with regards to your process lifetime.

  • Having a service around because… well, you thought that you needed a service, and so you put your code in one and started it, and then you couldn’t figure out when to stop the service, so you left it running… is NOT delivering value to the user. Do not create services because the mood strikes you. Create services because you are actively delivering value to the user when you are not in the foreground, not just “because”.

Usually, we have clear scenarios when we need to use the other three Android components:

  • Use an activity when you want to show something on the screen

  • Use a content provider when you want to share stuff with other apps that looks like a database or a stream

  • Use a broadcast receiver when you are looking to respond to a broadcast, usually issued by a third party

Services are a “Swiss army knife” that can be applied in lots of scenarios. As such, it is sometimes difficult to tell what is and is not a valid use of a service. If services did not have the “keep my process around” role, tossing services around willy-nilly would not be nearly as much of an issue. But services do impact the user and can reduce user satisfaction, and so it is important that developers think through why they are implementing a service, when that service should be running, and when that service should not be running.

And the simplest mantra that I have come up with to help developers work all that out is: “only have a service running when it is actively delivering value to the user”.

Jul 27, 2014

Gradle for Android Online Course

Just a reminder: I am teaching a half-day online course on behalf of Gradleware, covering the basics of Gradle for Android:

  • How to get it set up
  • How to get your first Gradle build file working
  • How the new project structure works
  • How to take advantage of build types and product flavors
  • How to integrate other projects and third-party code as dependencies
  • How to use Gradle for Android to automate other aspects of your project builds

You can see a larger outline of the virtual course on the Gradleware Web site.

The course is Tuesday, August 19, 2014, from 8:30am to 12:30pm (Pacific Standard Time).

Jul 21, 2014

Webinars: Memory and Security

A pair of webinar topics have been added to the roster for August through early October.

One is a two-hour Webinar on memory management, a trimmed-down and slightly-updated version of a presentation that I delivered at AnDevCon Boston 2014. Here we will examine why you have as many problems as you do, what tools exist to help you determine if you have a memory problem (and where that problem may come from), and some tips for better managing the memory that you have.

The other is “The Security Problems You Aren’t Thinking About”, reviewing a variety of ways in which your app could have security problems, particularly using APIs and Android capabilities that you might think would not present such problems. This is based in part on a conference presentation I delivered at DevWeek 2014 earlier this year.

As with past webinars, these are each scheduled for four different dates and times, to try to provide you with enough options to find one that you can attend.

Subscribers can visit their Warescription page to get the discount codes for these and the other scheduled webinars, though they are also open to the public.

If you have any questions regarding the webinars, let me know.

Jul 08, 2014

A Uri Is Not (Necessarily) a File

Google is advising developers to stop sending file:/// Uri values to other apps, whether via the data field in an Intent (e.g., ACTION_VIEW) or via an extra (e.g., EXTRA_STREAM with ACTION_SEND). There is no guarantee that the other app will have the ability to access that file, and making the file MODE_WORLD_READABLE is not great from a security standpoint.

Instead, you will increasingly need to handle the receipt of content:// Uri values.

Ideally, nowadays, those Uri values are “openable”. You can perform the following operations on such Uri values:

  • Open them via openInputStream() (and occasionally openOutputstream()) on ContentResolver

  • Find their MIME type via getType() on ContentResolver

  • Find out their DISPLAY_NAME and SIZE — the OpenableColumns — via a query() on the Uri (again, using a ContentResolver, or in theory a CursorLoader)

FileProvider from the Android Support library serves openable Uri values from files on internal and external storage. My StreamProvider serves openable Uri values from more locations, including assets and raw resources. The Storage Access Framework is designed around openable Uri values. And you can create your own ContentProvider that serves up openable Uri values.

The legacy convention was that a content:// Uri might not be openable directly. Instead, it pointed to a database row, retrievable via query(), and you would look in the _DATA column for how to access the actual data. Some providers no doubt continue to use this pattern. The rules for what the _DATA column would be were not well documented, but by convention they tended to be a path to a file. The problem is that this runs afoul of Google’s current guidance, as there is no guarantee that other apps can access such a file.

Do not blindly assume that if you get a content:// Uri that it is for the _DATA pattern. If you query() the Uri and do not get a _DATA value back, try using the Uri directly with ContentResolver. Or, perhaps do the inverse: try to open a stream on the Uri, and if that fails, then see if the _DATA pattern is in play.

An increasing percentage of the content:// Uri values you get will be openable, as more and more developers adopt FileProvider, the Storage Access Framework, and other things that adopt the openable-Uri pattern. Ignoring such Uri values, such as by crashing or rejecting a request when you do not find a _DATA value, will diminish the value of your app.

A hat tip to Stack Overflow users iforce2d and matiash for spurring me to do more analysis of some prominent Android apps that crashed when accessing FileProvider.

Jul 04, 2014

Consuming AARs from Eclipse

Increasingly, you will find that libraries for Android developers come only in AAR form. For example, many of the libraries that are part of the “L” Developer Preview have only been shipped to date in AAR form.

This is wonderful if you are using Gradle for Android, with or without Android Studio. It is less than wonderful if you are using Eclipse/ADT.

UPDATE: If you are using Eclipse/ADT with Maven, the android-maven-plugin supports AARs, which I had forgotten when I originally wrote this post. Many thanks to William Ferguson for slapping me with an appropriately-sized trout for missing that.

Fortunately, a StackOverflow user named FD_ came up with a basic recipe for converting an AAR into an Android library project, one that can be used from Eclipse/ADT. I have tested the recipe, have confirmed that it appears to work, and have created a Ruby script to automate the conversion.

The steps that my deaar script goes through are reminiscent of FD_’s recipe:

  1. UnZIP the AAR into some directory.

  2. Create an empty directory that will be the home for the Android library project. For the rest of these steps, I will refer to this as “the output directory”.

  3. Copy the AndroidManifest.xml, res/, and assets/ directories from the AAR into the output directory.

  4. Create a libs/ directory in the output directory. Copy into libs/ the classes.jar from the root of the unZIPped AAR, plus anything in libs/ in the AAR (e.g., mediarouter-v7 has its own JAR of proprietary bits).

  5. Decide what build SDK you want to try to use. You might just choose the highest SDK version you have installed. Or, you can use the android:minSdkVersion and the -vNN resource set qualifiers to get clues as to what a good build SDK might be. If desired, create a file with a target=android-NNN line, where NNN is your chosen build SDK. Or, you can address this in Eclipse later on.

  6. Import the resulting project into Eclipse, and if needed adjust the build SDK (Project > Properties > Android). Also, you will need to attach to this library project any library projects it depends upon (e.g., mediarouter-v7 depends upon appcompat-v7).

This is all based on the current AAR packaging, and therefore is subject to change.

Also, be sure to see if the AAR is natively available as an Eclipse-friendly Android library project, such as from an open source project’s repository. For example, all of my CWAC libraries ship as Eclipse-friendly Android library projects, in addition to AARs (and, for those not needing resources, JARs). This sort of AAR conversion should be an avenue of last resort, not the first thing you try.

If you try the deaar script and run into problems with it, please use the support options listed in the project’s README. If you run into AARs that do not appear to work using this script, drop me a line.

Jul 03, 2014

Older Posts