As a result of my work on IMAP IDLE support in Android's default mail application, I have been experimenting with various strategies for implementing long-lived services and persistent connections that operate efficiently in a variety of circumstances. Several quirks about Android and mobile devices in general arose that could be of value to anyone implementing similar services.
For most protocols, you will need to implement some type of client-initiated keep alive at the application layer. For my purposes with IMAP I simply complied with the RFC and elected to leave IDLE mode then re-enter after 28 minutes of inactivity. On Android, you must use the AlarmManager service to wake the CPU for this task. You might be tempted to use a Handler for timing or even a simple thread with a looped sleep() however it should be noted that unless your application otherwise holds a WakeLock you cannot rely on any timing mechanism other than the AlarmManager. Once the screen goes blank, the CPU may sleep and once it does other timing mechanisms will block until the CPU wakes up again, regardless of any timeout paramters you supply.
After running my test for several days I noticed Android was mysteriously killing processes, claiming that the services implemented in them have "died", then restarting them just a few minutes later. No call to the service's onDestroy method will occur, and even on service restart you will only see a call to onCreate and not onStart. In order to compensate for this you are expected to store your state persistently and check for a discrepency during onCreate and then invoke startService for yourself if necessary. The SharedPreferences system can be handy for this.
Source code for a functional demonstration on this topic can be found at my android-random project page, under the module TestKeepAlive.
Wednesday, January 7, 2009
Subscribe to:
Post Comments (Atom)
5 comments:
You comment that onDestroy is not called when your Service is killed, and this is completely consistent with the Service lifecycle. You can not count on onDestory being called for anything. You can count on onStop being called. For a service you should consider it dead after it returns from onStop. For an Activity you should consider it dead after onPause is called.
Cheers,
Justin
Android Team @ Google
I can see how your method keeps the TCP/IP session alive, but does it let you get asynchronous incoming data from the socket if the phone goes to sleep? Meaning, once the phone sleeps, before your 28 minute wake-up timer, will your Service get woken up in order to handle incoming data?
I'm working on K-9 email, and am interested in the various approaches to checking IMAP servers for new mail.
Daniel,
It's possible to set a PARTIAL_WAKE_LOCK with PowerManager. http://code.google.com/android/reference/android/os/PowerManager.html
Although, I'm not sure of the effect it would have on battery.
Chris,
Using a WakeLock, like I added to DAmail and K-9 for use with checking email while asleep, would have a huge impact on battery life. If the G1 is kept on using a WakeLock for ever, to allow IMAP IDLE traffic to come through at any time, the phone would not run long enough to be useful. The hard part, indeed, is to support IMAP IDLE without using a 100% duty cycle WakeLock.
Dan.
Daniel,
You do not need to do any special work to wake up when data is received on the socket. Even while sleeping, the radio is periodically listening for network events. If it didn't do this, it wouldn't make a very good phone :)
You will not require a WakeLock.
Post a Comment