Foreground Services
If you have a service that will run for a substantial period of time, there is a risk that your process will still be terminated. That could be triggered by the user, or it could be the OS’s own decision, based on the age of your process.
Generally speaking, this is a good thing for the user, because too many developers “leak” services, causing them to run unnecessarily, without adding value to the user, and tying up system RAM as a result.
But, what about services that are delivering value to the user for a long period? For example, what about a music player, where, in theory, the service is delivering value until the user presses some sort of “stop” button somewhere to turn off the music?
For those sorts of situations, you can flag a service as being a “foreground service”.
Isn’t “Foreground Service” an Oxymoron?
You might be forgiven for thinking that “foreground” and “service” are not designed to go together.
Partly, that is because we have overloaded the term “foreground”.
A foreground service is not one that somehow takes over the screen. A foreground service is one that runs with foreground priority. That means:
It will be treated similarly to the app that is in the UI foreground, from the standpoint of determining processes eligible for termination
It will be classified as foreground from a CPU standpoint, rather than being relegated to the standard background process group
The former is what many developers want: a service (and process) that will not go away.
The latter is what many users fear: a service (and process) that is capable of stealing chunks of CPU time away from the game, video, or whatever else is truly in the foreground from a UI standpoint.
Services themselves, while useful, are best when used sparingly, only running when they are actively delivering value to the user. “This goes double” for foreground services.
Putting Your Service in the Foreground
Putting a service into the foreground is a matter of calling startForeground(). This method takes two parameters, the same two parameters that you would pass to notify() of NotificationManager:
A prepared Notification
A unique ID for that Notification
Android will then display the Notification. So long as the Notification is visible, your app’s process will be given foreground priority.
You undo this by calling stopForeground(). stopForeground() takes a boolean parameter, indicating if the Notification should be removed (true) or not (false). Typically, you will pass true, so the Notification only clutters up the screen while you need it.
The Notifications/Foreground sample project is a clone of the Notifications/DownloadNotify sample that opened this chapter, adding in the use of startForeground() and stopForeground().
Towards the top of onHandleIntent(), we call startForeground(), to really ensure that our process will remain intact long enough to complete the requested download:
startForeground(FOREGROUND_ID,
buildForegroundNotification(filename));
This, in turn, uses a buildForegroundNotification() method to build the Notification that will be displayed while the service is categorized as being in the foreground:
private Notification buildForegroundNotification(String filename) {
NotificationCompat.Builder b=new NotificationCompat.Builder(this);
b.setOngoing(true);
b.setContentTitle(getString(R.string.downloading))
.setContentText(filename)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setTicker(getString(R.string.downloading));
return(b.build());
}
Note that we use setOngoing(true), to indicate that this is an “ongoing” operation. This precludes the user from removing the Notification manually, as doing that would drop our process out of foreground priority.
Towards the end of onHandleIntent(), we call stopForeground(), before calling raiseNotification():
stopForeground(true);
raiseNotification(i, output, null);
There is a similar stopForeground() call in the catch block that raises the failure Notification in case of an I/O error.
In both cases, we pass true to stopForeground() to remove the Notification. From the user’s perspective, we could just as easily have passed false, as the Notification used with startForeground() will also be removed once our service is destroyed, which will happen shortly after onHandleIntent() ends.