Previously in this series we have looked at various ways of moving slow, intensive, or blocking tasks off of the UI thread in order to keep our app responsive. The final mechanism that we’ll look at is Android services.
Services are a core feature of Android which are often misused, so we need to really understand what Services are and how best they should be used. I like to think of Services as a means of performing some task which is not attached to a specific Activity. If you want to do task which is intrinsically liked to an Activity, but you don’t want to run it on the UI thread, then I will generally use one of the mechanisms that we’ve discussed previously in this series. The cases where you should consider services are where you require a task to survive a transition from one Activity to another uninterrupted, or there is no currently active Activity from the app which the Service is a part of.
There are a large number of Task Killer apps in the Android Market, and I believe that this is due to two main issues: Firstly, many users do not understand that the OS will free resources automatically, and so they want a Task Killer which will perform the same function, only much more aggressively; Secondly, some developers implement Services using the same models as they would on the desktop / server which results in the OS having long-lived, sleeping Services in memory which really are not needed. It is precisely these poorly implemented Services that will suffer most from being forcibly killed by the OS or a Task Killer. Well implemented Services will fare much better.
There are two types of Service: foreground and background. A background Service is the default type of Service and can be killed by the OS (or Task Killer apps, more of them later) to free up system resources. A foreground service will not be killed by the OS, but requires an ongoing Notification to be displayed in the Notification bar while it is running so that the user is aware that the foreground Service is active and will not be killed automatically by the OS.
The obvious use-case for foreground Services is something like a music player where playback can continue when the app is closed, but the Service needs to be protected from being killed as this would halt playback. Foreground Services can be very useful, but as developers we need to make sure that we only use them where absolutely necessary. When deciding whether to use a foreground Service, the question that needs to be asked is: Will it immediately annoy the user if this Service was to be killed by the OS? The key word here is “immediately”. In the music player example, playback stopping because the Service was killed by the OS will almost certainly annoy the user and, most likely, prompt him to look for a different music player app.
When using foreground Services we must be careful to only use them for as long as we need to. I recall a popular music app (which I won’t name because the issue has long since been fixed) which used a foreground Service for music playback, but kept the foreground Service running permanently irrespective of whether music playback was active or not. Consequently the ongoing Notification was permanently in the notification bar unless the user went in to the application and selected “Quit” from the menu. This got a lot of negative reviews in the Android Market because users simply did not want the notification there when music was not playing. An update to the app to close the Service automatically when playback stopped, and consequently remove the ongoing Notification, improved the reviews quite considerably.
Many developers are worried about background Services for precisely the reason that they could be killed by either the OS or a task killer. On the desktop or sever, we are used to having background processes which will simply sleep while dormant and wake up periodically to do something before going back to sleep again. On mobile devices we have to be more efficient with system resources, so we need to adopt a similar approach as with foreground Services and shut down our Services while they are dormant. Shutting down a service is simply a matter of calling stopSelf(). Android provides us with the BroadcastReceiver mechanism which allows us to be woken up by specific Intents including ones caused by system events. Our BroadcastReceiver can call startService() to start up our Service. Also the PendingIntent mechanism can be used to wrap an Intent which will be sent, the Service can do something in response to the event that is indicated by the Intent that is received, and then shut down again. If our Services behave in this way, then they are extremely unlikely to be killed prematurely by either the OS or a Task Killer.
A common mistake with background Services is to keep the Service in memory and use a Timer or TimerTask to periodically perform some task. This is a really good way to get your Service killed by either the OS or a Task Killer. Your Timer gets killed along with your Service, and so your periodic task is killed permanently. A much better solution is to use AlarmManager to create an alarm which will send an Intent at some time in the future using the PendingIntent mechanism. The PendingIntent will cause our Service to be started, it does its processing and then shuts down again. If a repeating alarm is used, then it doesn’t matter as much if the Service is killed during an individual Service invocation because the alarm remains active and will continue to start the Service at regular intervals.
There are one or two cases where there really is no alternative to having a long lived Service. An example of this is a mail client implementing IMAP IDLE which needs to hold a socket connection open permanently. In this case, shutting down the Service will result in the socket being closed, so it is necessary to have the Service running permanently. That said, cases such as this are actually much rarer than you may think and you should always look for a solution where you can shut the service down when it is not active.
A really useful subclass of Service is IntentService. This provides us this a base class which will receive a single Intent and then shut down automatically once we have processed it. This really helps us to keep our Service behaving well. In the concluding part of this series we’ll look how to implement and use IntentService.
© 2012, Mark Allison. All rights reserved.
Background Tasks – Part 5 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.