Power on a cell phone is like water in a desert. It’s the very stuff of life. If you take the same naive programming techniques you learned when programming on a server in a datacenter your cell phone will die of thirst.
This is dramatically shown by Reto Meier, Tech Lead for the Android Developer Relations Team, in a remarkable series of instructional videos:
Remarkable because these videos are short, to the point, and chocked full of useful ideas and techniques. Though Android is targeted specifically, most of content should be generally useful.
As an example of how server programming differs from mobile programming, on a server copying a file from one server to another server is usually a one or two liner. Read a block of data from a file descriptor and write it to another file descriptor. It's a synchronous process. You can mess with block sizes and other tricks, but that's the basics. Using this naive natural as nature “sipping” type logic on a cell phone is wrong, it's the Little Cookie approach we'll talk about later. The problem is it drains the phone battery. Why? The cell radio will be on continuously. Why? You’ll learn that a bit later, but what you need to do is minimize radio usage by batching and properly scheduling transfers. You are not in Kansas anymore.
Which brings us to the...
Key idea: The cell radio is one of the biggest battery drains on a phone. Every time you send data, no matter how small, the radio is powered on for up for 20-30 seconds. Every decision you make should be based on minimizing the number of times the radio powers up. Battery life can be dramatically improved by changing the way your apps handle data transfers. Users want their data now, the trick is balancing user experience with transferring data and minimizing power usage. A balance is achieved by apps carefully bundling all repeating and intermittent transfers together and then aggressively prefetching the intermittent transfers.
How does a cell radio work?
To minimize radio usage and thus minimize power usage you must first understand how a cell radio works. The videos do a great job explaining all this, but here’s the gist:
Cell radios are controlled by a state machine that tries to balance low latency and longer battery life. Cell radios are not kept on permanently. They enter different states as a means of saving battery life, but must go to full power when sending data over the radio.
Begins in Standby mode where it draws minimum power until an app initiates a data transfer.
To send data the radio transitions to Full Power mode, a process that takes 2 seconds before performing the transfer. It will remain in full power mode for a set tail time just in case more data needs to be transferred. This avoids the ramp up time to Full Power state. State transitions themselves are a significant power drain so need to minimized.
If nothing happens in a 5 to 10 second tail state it transitions to an intermediate Low Power state where it uses less power and has a shorter transition time to Full Power.
If nothing happens for another 30-60 seconds it will drop back down to Standby.
Exact latencies and tail times vary by carrier, network, and device.
It should be clear that if you send data naively on a cell phone you are screwed. Which brings us to the two models of how to transfer data:
Big Cookie Model: When scheduling downloads download as much as you can, as infrequently as possible, minimizing the number of transfers and maximizing bandwidth usage.
Little Cookie Model: Transfer as little data as possible and perform transfers more frequently.
Who is the winner? Big Cookie. Little Cookie heavily fragments radio use. For every data transfer the radio stays on for 5 seconds at full power followed by 10-60 seconds at a lower power state before returning to Standby. Every time you transfer data you are powering the radio for at least 20 seconds. So sending small amounts of data frequently is the best way to drain a battery. Let’s say you send analytics data every 15 seconds and the the user clicks on a link intermittently. The result will be the radio is on continuously. So don’t do that.
What should your app do?
The upside of knowing about the cell state machine is that if you work with it you can balance application latency and power usage. The videos go into a number of techniques to bring about balance:
Minimize the number of radio state transitions.
Determine your applications battery usage profile
Generate graphs using: Logcat logging / Application Resource Optimizer / Network Statistics in DDMS
Analyze graph for battery inefficiencies
Look for regular pattern of transfers. This will cause regular radio usage and power drain. Shorter the period between updates the greater the power drain.
Look for short spikes in height or duration. These could be be batched together or prefetched.
Prefetch data for the next 2-5 mins (1-5mb)
Decreases latency and improves battery performance. By downloading all the data a user is likely to need in a single burst over a single connection at full capacity, the number of radio activations is significantly reduced.
Challenge is figuring out what to download and when and not wasting power by downloading data that’s never used.
On a 3G you can prefetch in 6 seconds enough information for a user session of 2 to 5 minutes of app usage, which is 1 to 5mb of data. If that data has a 50% chance of being used in the current session the cost of downloading the unused data matches the potential savings lost by not downloading that data to begin with. As the likelihood of the data being used increases the value of prefetching more data increases.
Not every network transfers data at the same rate so the equation will change based on the speed and efficiency of the network. The size of the prefetch cache must be increased or decreased based on the speed and cost of each network. On a faster 4G network significantly more data must be prefetched to account for the higher amount of data that can be downloaded and the higher battery cost of 4G.
It’s more efficient to have transfers occur when a radio is in its active state, so if a time sensitive transfer is initiated, look to preempt, by transferring the data now, any transfers that will need to occur in a few minutes.
For example, if an article needs to be fetched for a user to read, it’s a good time to also prefetch other content the user might read in the next few minutes.
For a music player, maintain a buffer of 1 song + the song being played. This balances against downloading an entire album because it probably won’t be listened too.
For a news reader, the naive way is to download top level and thumbnails, this keeps the radio constantly busy. Instead, download the first set of headlines and thumbnails and article text and the get the next batches later. You can try either depth first or breadth first strategies. A better approach is to use science. Keep track of what your users and their friends read to predict what they might read and therefore what you should prefetch. Or you can prefetch everything, which is expensive if the data is never used.
Use Device State to schedule downloads when battery life and bandwidth aren’t as important like when it’s charging.
Use the current activity of the user to modify the aggressiveness of prefetching. When the app is open and the user is standing still then prefetch more.
Generally don’t prefetch when an app is in the background.
Don’t delay an app from starting. Don’t use a splash page. Process data in the background concurrently to minimize startup latency.
Use HTTP live streaming where possible. It transfers data in bursts rather than a continuous stream which would keep the radio constantly active.
Batch and bundle all non-time critical transfers.
Transfer the batch the next time a time sensitive operation is performed.
When repeating events must be sent randomize the periodicity
If an operation is not time sensitive, say uploading an image, it might be better to wait 30 seconds just in case another image or piece of data also needs to be uploaded (see SyncAdapter)
Eliminate client-side polling
Use Google Cloud Messaging. Data is only sent to your device when there’s data to send. So no polling loops. Gives lower latency and better battery usage.
Transfer less data, less often
Eliminate the (need for a) refresh button.
Reduce updates based on app use.
Create a batch queue to which you can add delay tolerant transfers. The next time you execute an on demand transfer you can also transfer all the queued data. Data can be lost if the app is closed before transfers occur. The way to get around this is add the data to a local database and query the database for pending transfers. The data in the database will stick around after the app is closed. Android has something called SyncAdapter to make this process much easier on the programmer. It implements all these best practices.
The videos go into all of this in more detail, especially on the SyncAdapter, and they were well worth watching.
These videos make clear what may have not been clear before: programming cell radio based mobile devices is a specialized domain that takes some specialized knowledge and techniques to do well. If you've always wondered why apps use so much power and your battery doesn't last as long as it should, it's easy to see why.
From the Comments
iOS7 Notes
Plorkyeran: iOS finally added background data fetching in iOS 7, but it's very heavily restricted (the OS chooses when apps get woken up to fetch, not the app, and in practice it can be as rare as once a day). Other than that, only a very limited number of categories of apps get to do stuff in the background (such as music players, voip apps, and location trackers), and they actually do reject apps which claim to qualify for the exemption but don't.
dzamir: In fact iOS7 uses the same strategy described in this post: it wake ups all the applications that require a background download at the same time to minimize the time the radio is on.
Isn't Worrying About Power Just a Case of Premature Optimization?
miratrix: The difference between DCH (the full-on high power state) and PCH (the lowest power, waiting-for-paging state) is about 2-orders of magnitude. Last time I measured, it was about ~100mA vs ~1mA (at ~3.7V) used by the radio. So, it's not couple of minutes of battery life, but rather _many_ hours of standby life instead. People usually aren't very happy when a fully charged phone dies overnight.
I've seen apps do some crazy things and it really has a significant effect in over-all battery life. A popular Android weather clock widget woke the phone up every minute to update the minute number on the graphics and updated the weather information every ~15 minutes (gps + radio!) which single handily crushed the standby battery life from multiple days to less than 8 hours.
Yes, I don't think _every_ decision should be based on minimizing the wake ups... but on the other hand, all developers should at least try to have as much understanding of the platforms that they're working on so that they know what trade-offs they're making with each feature they're adding.
kintamanimatt: Respecting known limitations and working within best practices is absolutely not premature optimization. If you know that using the mobile radio in a certain way is a source of excessive battery usage, it's silly to just disregard this information. It's not premature optimization if you know where the performance issues are from the outset, and these performance issues are so incredibly common that the Android team put out videos about them.
The mobile radio will eat your battery very quickly, and probably chews through as much power as your screen. If you want to see how expensive it actually is, disable fast dormancy†. A slightly less brutal demonstration can be had by opening an OpenVPN connection. The keep-alive packets will keep your mobile radio in a higher power state, pretty much in the same way some disrespectful apps do and you'll (quite unsurprisingly) see your battery drain faster.
A badly written app can drain hours of battery charge, not minutes.
Related Articles