Using the wireless radio to transfer data is potentially one of your app's most significant sources of battery drain. To minimize the battery drain associated with network activity, it's critical that you understand how your connectivity model will affect the underlying radio hardware.
This section introduces the wireless radio state machine and explains how your app's connectivity model interacts with it. It then offers several techniques which, when followed, will help minimize the effect of your app’s data consumption on the battery.
The radio state machine
The wireless radio on your user's device has built-in power-saving features that help minimize the amount of battery power it consumes. When fully active, the wireless radio consumes significant power, but when inactive or in standby, the radio consumes very little power.
One important factor to remember is that the radio cannot move from standby to fully active instantaneously. There is a latency period associated with "powering up" the radio. So the battery transitions from higher energy states to lower energy states slowly in order to conserve power when not in use while attempting to minimize the latency associated with "powering up" the radio.
The state machine for a typical 3G network radio consists of three energy states:
- Full power: Used when a connection is active, allowing the device to transfer data at its highest possible rate.
- Low power: An intermediate state that cuts battery power consumption by around 50%.
- Standby: The minimal power-consuming state during which no network connection is active.
While the low and standby states drain significantly less battery, they also introduce significant latency to network requests. Returning to full power from the low state takes around 1.5 seconds, and moving from standby to full power can take over 2 seconds.
To minimize latency, the state machine uses a delay to postpone the transition to lower energy states. Figure 1 uses AT&T's timings for a typical 3G radio.
The radio state machine on each device, particularly the associated transition delay ("tail time") and startup latency, will vary based on the wireless radio technology employed (3G, LTE, 5G, and so on) and is defined and configured by the carrier network over which the device is operating.
This page describes a representative state machine for a typical 3G wireless radio, based on data provided by AT&T. However, the general principles and resulting best practices are applicable for all wireless radio implementations.
This approach is particularly effective for typical mobile web browsing as it prevents unwelcome latency while users browse the web. The relatively low tail-time also ensures that once a browsing session has finished, the radio can move to a lower energy state.
Unfortunately, this approach can lead to inefficient apps on modern smartphone operating systems like Android, where apps run both in the foreground (where latency is important) and in the background (where battery life should be prioritized).
How apps impact the radio state machine
Every time you create a new network connection, the radio transitions to the full power state. In the case of the typical 3G radio state machine described earlier, it will remain at full power for the duration of your transfer—plus an additional 5 seconds of tail time—followed by 12 seconds at the low energy state. So for a typical 3G device, every data transfer session will cause the radio to draw energy for at least 18 seconds.
In practice, this means that an app which makes a one second data transfer, three times a minute, will keep the wireless radio perpetually active, moving it back to high power just as it is entering standby mode.
By comparison, if the same app bundled its data transfers, running a single three-second transfer every minute, this would keep the radio in the high-power state for a total of only 20 seconds each minute. This would allow the radio to be on standby for 40 seconds of every minute, resulting in a significant reduction in battery consumption.
Now that you understand how network access affects battery life, let’s talk about a few things that you can do to help reduce battery drain, while also providing a fast and fluid user experience.
Bundle data transfers
As stated in the previous section, bundling your data transfers so that you’re transferring more data less often is one of the best ways to improve battery efficiency.
Of course, this is not always possible to do if your app needs to receive or send data immediately in response to a user action. You can mitigate this by anticipating and prefetching data. Other scenarios, such as sending logs or analytics to a server and other, non-urgent, app-initiated data transfers, lend themselves very well to batching and bundling. See Optimizing app-initiated tasks for tips on scheduling background network transfers.
Prefetching data is another effective way to reduce the number of independent data transfer sessions that your app runs. With prefetching, when the user performs an action in your app, the app anticipates which data will most likely be needed for the next series of user actions and fetches that data in a single burst, over a single connection, at full capacity.
By front-loading your transfers, you reduce the number of radio activations required to download the data. As a result, you not only conserve battery life, but also improve the latency, lower the required bandwidth, and reduce download times.
Prefetching also provides an improved user experience by minimizing in-app latency caused by waiting for downloads to complete before performing an action or viewing data.
Let's look at a practical example.
A news reader
Many news apps attempt to reduce bandwidth by downloading headlines only after a category has been selected, full articles only when the user wants to read them, and thumbnails just as they scroll into view.
Using this approach, the radio will be forced to remain active for the majority of users' news-reading session as they scroll headlines, change categories, and read articles. Not only that, but the constant switching between energy states will result in significant latency when switching categories or reading articles.
A better approach would be to prefetch a reasonable amount of data at startup, beginning with the first set of news headlines and thumbnails—ensuring a low-latency startup time—and continuing with the remaining headlines and thumbnails, as well as the article text for each article available from at least the primary headline list.
Another alternative is to prefetch every headline, thumbnail, article text, and possibly even full article pictures—typically in the background on a predetermined schedule. This approach risks spending significant bandwidth and battery life downloading content that's never used, so it should be implemented with caution.
While prefetching data carries a lot of benefits, used too aggressively prefetching also introduces the risk of increasing battery drain and bandwidth use—as well as download quota—by downloading data that isn't used. It's also important to ensure that prefetching doesn't delay application startup while the app waits for the prefetch to complete. In practical terms that might mean processing data progressively, or initiating consecutive transfers prioritized such that the data required for application startup is downloaded and processed first.
How aggressively you prefetch data depends on the size of the data being downloaded and the likelihood of it being used. As a rough guide, based on the state machine described earlier, for data that has a 50% chance of being used within the current user session, you can typically prefetch for around 6 seconds (approximately 1-2 megabytes) before the potential cost of downloading unused data matches the potential savings of not downloading that data, to begin with.
Generally speaking, it's good practice to prefetch data such that you will only need to initiate another download every 2 to 5 minutes, and in the order of 1 to 5 megabytes.
Following this principle, large downloads—such as video files—should be downloaded in chunks at regular intervals (every 2 to 5 minutes), effectively prefetching only the video data likely to be viewed in the next few minutes.
One solution is to schedule the full download to occur only when connected to Wi-Fi, and possibly only when the device is charging. The WorkManager API supports exactly this use case, allowing you to restrict the background work until the device meets the developer-specified criteria, such as charging and being connected to Wi-Fi.
Check for connectivity before making requests
Searching for a cell signal is one of the most power-draining operations on a
mobile device. A best practice for user-initiated requests is to first check for
a connection using
ConnectivityManager, as shown in
Monitor connectivity status and connection
If there’s no network, the app can save battery by not forcing the mobile radio
to search. The request can then be scheduled and performed in a batch with other
requests when a connection is made.
An additional strategy that can help in addition to batching and prefetching, is to pool your app's network connections.
It's generally more efficient to reuse existing network connections than it is to initiate new ones. Reusing connections also allows the network to more-intelligently react to congestion and related network data issues.
HttpURLConnection and most HTTP
clients, such as OkHttp, enable
connection-pooling by default, and reusing the same connection for multiple
Recap and looking ahead
In this section, you learned a lot about the wireless radio and some strategies that you can apply broadly to provide a fast, responsive user experience while reducing battery drain.
In the next section, we’ll take a detailed look at three distinct types of network interactions common to most apps. You’ll learn the drivers for each of these types as well as modern techniques and APIs for managing these interactions efficiently.
Minimize the effect of regular updates