Monday, February 28, 2011

Persistent services in Android

Being an Android service is a little like being in a Final Destination movie. You'll be minding your own business, doing all sorts of useful stuff in the background, then with no warning the Activity Manager decides to push you down an elevator shaft onto some pointed objects that have no business being there.

OK, so there is a solid justification for why Android kills background services all the time. It's a mobile OS, so performance and battery life are a big concern. But that doesn't stop a lot of people from asking, "How can I keep my services from getting killed?"

The general consensus seems to be that it's not possible, which is mostly true. But there are a few options available.

If you're a system developer (like I am at the moment), you're really in luck. The <application> tag in the Android manifest has a poorly-documented but extremely useful attribute, android:persistent. Setting this to "true" makes your app literally impervious to death...with 2 important caveats:
  1. It only applies to system apps (ie apps in /system/app). The flag is ignored for apps in /data/app. So as I said, this is only useful if you're developing an Android system, or maybe if you're manually installing an app on a rooted phone. Marketplace apps are right out of luck.
  2. Persistence only applies to services in the default application process. If any <service> tag includes an "android:process" attribute that causes it to run in a separate process, that process will NOT be persistent.
You can check persistence with the adb shell command "dumpsys activity". Persistent processes will be marked with "PERS":
> adb shell dumpsys activity | grep PERS
    PERS #17: adj=sys  /F ae27b4d0 340:system/1000 (fixed)

Obviously this should be used with extreme care. Any non-critical tasks in your app should be moved to a separate service with the "android:process" attribute set, so that those particular bits are not persistent.

For Marketplace developers...sorry, but your services still have a bullseye on them. It's not all bad though; there are 3 solid (and well documented) solutions that should suffice:
  1. If you need to perform a task repeatedly at set intervals (ie polling for data), set an AlarmManager to spawn your service as needed.
  2. If you really need to remain running constantly (ie to maintain music playback or something else with zero interruptions), call startForeground(). In newer Android flavors, you'll have to provide a Notification to alert the user that your service is running...but that's not much to ask.
  3. If you need to remain running constantly but can endure short interruptions, just return START_STICKY from your onStartCommand() method. If Android does kill your service, it will be restarted ASAP (with calls to onCreate and onStartCommand). In my experience it's rarely more than a minute of downtime, but this could vary.
    Note: if you are writing an older app that uses onStart instead of onStartCommand (or if you return the START_STICKY_COMPATIBILITY flag instead), your service will be restarted with a call to onCreate only (onStart will not be called for a restart).

20 comments:

  1. Your post is really helpful to me.
    Thanks!

    ReplyDelete
  2. in older apps the service don't get restarted

    ReplyDelete
  3. what would be your suggestion for a service that reads GPS data at a rate of 1/sec for at least 3-4 hours uniterrupted.

    Really need this to run in the background to collect very important data, I can afford a few second interuption every once in awhile.

    My service model will be killed usaully after 30 minutes to an hour.

    any help would be appreciated

    ReplyDelete
    Replies
    1. You might try setting an AlarmManager to fire at 1s intervals. This does assume that your code takes less than 1s to run, though, which might not be feasible. Alternately, you'll just have to call startForeground() and make your service sticky so that it gets restarted whenever possible.

      Delete
  4. As a user, is there anyway to at least increase priorities of an app, so you can choose the ones you want to keep the longest?

    ReplyDelete
    Replies
    1. Sorry, there's no way for a user to set priorities. Android makes the call based on a number of factors like how long the process has been running, whether it's bound to a foreground activity, etc.

      Delete
  5. Thanks! It's really helpful!

    ReplyDelete
  6. This is a very useful article, not much info on the net on this subject; however, I don't get one thing - what is the "default application process" you mentioned in 2.) ? I'm trying to implement a resident service that won't be killed by Android Task Killer or custom Task Killer applications.

    For now I found 2 ways for doing this:
    1. Implement native application and register it into init.rc to be started automaticaly
    2. Implement the app as a service started by System Server, but this method requires rebuilding the framework, which is highly undesirable

    And your method, of course.

    ReplyDelete
    Replies
    1. By "default application process", I mean the value specified for android:process in the tag (see http://developer.android.com/guide/topics/manifest/application-element.html#proc). If any activity or service tag in the manifest has a different android:process value, persistence won't extend to that process. In practice, most developers don't need to use this attribute, so it's generally not an issue - but I wanted to call it out.

      Delete
    2. Thank You for calling this out! It was a very important detail in my case.

      Delete
  7. For a /system/app process that has android:persistent set to true, is there any way to exit that process if it is to be configurable? I haven't found a way that doesn't end up with
    a message like:

    I/ActivityManager( 349): Start proc foobar for restart foobar: pid=24676 uid=10131 gids={3003, 1007}

    after foobar tries to exit.

    ReplyDelete
  8. This is very useful

    ReplyDelete
  9. you wouldnt happen to have an example of alarmmanger waking up service once a day
    ?

    ReplyDelete
  10. Thank you very much.
    i am a new system developer for android. We develop custom lockscreen. But if some reason, our app can be crashed with exception, and stopped.

    i assume that how to prevent our service for all conditions?
    Does "android:persistent" work even our app crashed and stopped?(i mean that will "android:persistent" restart our application when it crashed and stopped)

    ReplyDelete
    Replies
    1. The "android:persistent" flag just tells the system never to purposely kill your app, even when resources get low. It doesn't affect whether your app is restarted after a crash.

      As far as I know, there is no way to automatically restart an activity after it crashes. You can do it from a service by returning START_STICKY from onStartCommand - I believe this will cause the service to be restarted even if it crashes.

      Delete
  11. I ran a few tests and it seems that for a "persistent" system app, you can still "Force Stop" it from the Application Manager, but the OS will immediately restart the process. Could you confirm this?

    ReplyDelete
    Replies
    1. Yes, this is correct. As far as I know there is no way to prevent the user from using "Force Stop". However, sticky services will quickly get restarted.

      Delete