i.e. things to avoid
This week: scheduling future network activity
The basic principle: never(1) schedule future network activity with a non-wakeup alarm, or with a simple deferred message or Runnable on a Looper. [(1) Never? Well, hardly
ever. See below for possible exceptions to think about.]
This isn't quite obvious. Non-wakeup alarms & Looper are attractive because you feel like you're conserving battery. You aren't waking up the device in someone's pocket; you're just taking advantage of the next time that the device is in use anyway, so you're keeping things lean, right?
Well, that's only part of the story. The other part is that you're not the only app on the phone, and that device isn't the only one on the network.
Users turning on the screen, directly using the device, are not the only time that non-wakeup alarms get fired, or that Looper-based deferred messages get a chance to run. _Anything else_ that wakes up the device, even without turning on the screen, will also cause any pending non-wakeup alarms etc to be delivered. "Anything else" can mean, say, the alarm clock going off at 7:00 am....
... along with literally millions of other people who also
set their alarms for 7am. And maybe your app is pretty popular, and so you have a hundred thousand people using it out of that pool of 7am-wakeup folks. *Boom.* If your app was waiting for a chance to do something on the network, you just hit the cell network with a hundred thousand requests within the space of a second or two. Deferred wakeups tend to happen in response to these kinds of external factors such as time of day. Putting any kind of load on external resources (e.g. the network or your remote servers) when this kind of deferred wakeup happens is going to cause load spikes, guaranteed. That's bad, and it's your fault.
There are a few different approaches to take that avoid this problem. Pick the one that is a good fit for the kind of activity that your app does.
First off, instead of using non-wakeup alarms or simple deferred Looper messages, you could use a real wakeup alarm and make sure to "fuzz" either the interval between your wakeups, or the times at which you want to wake up. By making sure that your app's activity on different physical devices is timed differently, you are being a good citizen by preventing load spikes on your users' carrier, your remote servers, etc.
You could even just layer a "fuzz" factor on top of your existing deferred wakeup architecture. When your non-wakeup alarm fires, or when your deferred Looper message is handled, don't immediately perform your network activity. Instead, set a one-shot wakeup alarm for a small random time in the future -- say, from 0 to 300 seconds, to pull numbers out of a hat. An extra five minutes probably isn't going to hurt -- after all, you're just running at the whim of the device's other activity anyway -- and even a few minutes of fuzz makes a lot of difference in terms of preventing load spikes on the network. This isn't as efficient as the first option of using exact timing with random intervals, because there will still be a "bump" in activity following whatever external factor caused the initial wakeup, but it's a lot better than doing nothing.
You could also consider using the Alarm Manager's "inexact" repeating alarm API <http://developer.android.com/reference/android/app/AlarmManager.html#setInexactRepeating(int
, long, long, android.app.PendingIntent)>. These are wakeup alarms, but the battery impact of them is mitigated by "batching" the inexact alarms from multiple applications to all be processed at once, within a single wakeup of the device. The batching is not tied to wall-clock time and will vary across physical devices specifically to prevent spiking external resources.
[Edited to add] Finally, many apps will best be served by using Google Cloud Services. The server-side implementation of GCM can do quite a bit to optimize battery and network usage patterns on the phone by multiplexing several applications' activity together, minimizing wakeups and radio power up/down cycles, etc.
Hopefully this has gotten you thinking; as always, feel free to discuss and ask questions.Edit:
In conversation after I'd written this, it was pointed out that sometimes performing network access in this "immediately upon wakeup" deferred manner really, truly is the right thing to do. A good example is something like a Twitter application. The best possible user experience is that if the user pulls their device out of a pocket, unlocks it, and heads straight to the app, the content they're presented with is as up-to-the-minute as possible. If the app waited to become the foreground visible application before sending out its network request, that's not going to happen. If the app tries to stay up to date while the phone is asleep, it means it'll burn a lot
of battery keeping the data radios spun up all the time. In this specific kind of case, the implementation I'm warning against here is likely to give the all-around best user experience.
One moral here is that there aren't many hard-and-fast rules; the needs of the user and of the app are paramount, and what is less-than-ideal in most cases might really be the best approach in certain others. Always think hard about how your app will behave, what your users will expect and appreciate, and what the tradeoffs are.