January 27, 2015

Diamonds Are Forever. Services Are Not.

diamond2Android offers a “service” component. Unlike “activities” (screens) that interact directly with the user, services are more for managing background operations. The quintessential example is a music player, continuing to play tunes while its UI is not running.

Some developers then make the leap from “it is possible” to “it is a really good idea and should be used wherever”. Many people on Android Google Groups or StackOverflow propose to create services that “run forever” and ask how to prevent them from being killed off, and such.

These developers appear to ignore the costs of this approach:

  • While running, a service consumes one process’ worth of memory, which is a substantial chunk, despite Dalvik’s heavy use of copy-on-write techniques to share memory between processes. A single service hogging memory is not that big of a deal; a dozen such services will prevent the Android device from being usable. Hence, even if the memory footprint does not impact the developer directly, it impacts the users indirectly — much like how pollution benefits the polluter with a corresponding cost to society.
  • While running, the service will need to fight Android, which will want to shut down the service if RAM gets too tight. While Android should, in principle, restart the service once memory is plentiful again, the developer will have no control over the timing of this.
  • While running, the service will need to fight its own users, who may elect to shut down the service via the Manage Applications screen, or through a third-party task manager. In this case, Android will not restart the service automatically.
  • The service will fall asleep when the device falls asleep and shuts down the CPU, which means even if the service “runs forever”, it will not be able to run forever, unless it prevents the CPU from stopping, which wrecks battery life.

The recommended alternative is to use AlarmManager, as described in a previous post and in finer Android programming books. Think of your Android service as a cron job or Windows scheduled task, not as a persistent daemon or Windows service. This approach works much better with Android’s framework and device limitations:

  • The service only uses memory when it is actually doing something, not just sitting around waiting.
  • The odds that Android will need to kill off the service decreases, in part because the service will not be “old” and other services are likely to be killed first.
  • The odds that the user will need to kill off the service decreases, because the service is less likely to cause any pollution that may cause the user problems.
  • The service can hold a WakeLock for the duration of its bit of work to keep the CPU running for a short period

Now, the AlarmManager API is not the friendliest. Some developers get tripped up while trying to handle multiple outstanding alarms. While there are ways to deal with this (via careful construction of PendingIntent objects), sometimes you do not truly need more than one alarm. If you have code that you want to run every 15 minutes, and other code that you want to run every 24 hours, use one 15-minute alarm and trigger the once-every-24-hours code every 96th time the 15-minute alarm goes off.

I encourage Android developers to try to avoid long-running services wherever possible. If you are uncertain how to design an app to avoid the long-running service, post a clear description of the business scenario (not just low-level technical stuff) to the [android-developers] group or to StackOverflow with the #android tag. We can try to help you find ways of achieving your business objectives in an Android-friendly fashion.



19 Comments

  1. ogwilson
    Reply

    Great read. Glad to see you guys covering the development paradigm of Android. Hopefully developers take heed to these suggestions: the app ecosystem, as I like to call it, on local hardware is very important to the overall android experience.

  2. @GodsMoon
    Reply

    Thanks for the great article. I'm waiting on a response currently about my app. http://groups.google.com/group/android-developers

    I don't think I can get around using a service for main part of my app, but currently I'm spawning a new thread every second to check which process has the highest priority and if it is acore then I show the lock screen. I would love to get rid of this 2nd part, but I don't think I can without some API changes.
    Either a change in the keyguardmanager class or a broadcast intent with the current activity/process when it changes.
    If you know of another way, I'd love to hear about it.
    Thanks for your help.

  3. Édouard
    Reply

    Totally agreee with you Mark, and I think that we actually need an indicator besides any Android application description, which tells whether it runs or not persistent services in the background. I would even add that the developer can schedule an alarm to start a service, and that this service can be stopped by itself to stop on demand, when it has performed its work!

  4. Shafqat
    Reply

    Is it possible to create a service that will missuse the class of Player?

    A service that runs for ever, but will not get killed because, you arel using the Player class to run for example and empty mp3, set it on repeat for ever?

    Is this possible?

  5. Andrew
    Reply

    Nice article – I think it’s excellent that Android forces developers to put a little more thought into WHY a background service is really needed. Restructuring it to work within Android’s eco-system probably results in a much more efficient app overall.

    I think there are business cases for some system apps to maintain persistent background services (ie a phone replacement app). I just wrote up some notes about different levels of persistence, drawing from this article and a few others. I’m hoping it helps other developers that are exploring persistent services on Android.
    http://engineersaredumb.blogspot.com/2011/02/persistent-services-in-android.html

  6. Lancer
    Reply

    Depending in what your service is doing, there is no problem in running it forever. I am making an app that lets the user start or stop the service at any time. I then use a BroadcastReceiver to shut the really draining operations down while the screen is off. It is then turned back on when the screen is turned on. I have run this app continuously for over 24 hours with pretty much no ill effects on battery life. Android assistant reported that it had only used .69 of the overall battery life, not bad for a service that runs forever.

    • Jacob Godserv
      Reply

      His same argument applies, though. When you turn the screen on, is the user going to be affected by a dozen services starting all at once? Sounds to me like you haven’t addressed the memory consumption issue.

  7. One
    Reply

    Why are you such a dickhead, in every comment that people ask on stackoverflow you write some shits about ”don’t do this don’t do that”.. like you’re trying to be extra smart, it’s really annoying.
    chill out a little bit..

POST A COMMENT.