The network traffic generated by an app can have a significant impact on the battery life of the device. To optimize that traffic, you need to measure it and identify its source. Network requests can come directly from a user action, from your own app code, or from a server communicating with your app.
This topic shows you how to monitor and categorize your network traffic, and provides guidance on identifying and resolving issues.
Use the Network Profiler to monitor requests
Use the Network Profiler to track your application’s network requests. You can monitor how and when your app transfers data and optimize the underlying code appropriately.
By monitoring the frequency of your data transfers and the amount of data transferred during each connection, you can identify areas of your application that can be made more battery-efficient. Generally, you will be looking for short spikes that can be delayed.
To better identify the cause of transfer spikes, the Traffic Stats API enables
you to tag the data transfers occurring from a socket within a given thread
using
TrafficStats.setThreadStatsTag()
.
Calling this function doesn't automatically tag all traffic for a particular
thread; the tags have to be applied to the sockets.
Once the thread tag is set, you can manually tag and untag individual sockets
using
TrafficStats.tagSocket()
and
TrafficStats.untagSocket()
.
A tag is also applied if a socket is opened on the thread, or if a server socket
accepts a connection.
Concurrent access to the same socket by multiple threads will use whatever tag the socket had when the network packets were sent or received (which may be different from when the user wrote or read the data, due to buffering and retransmits).
For example, you can define constants to represent different types of network traffic, as shown in the following code sample:
Kotlin
const val USER_INITIATED = 0x1000 const val APP_INITIATED = 0x2000 const val SERVER_INITIATED = 0x3000
Java
public static final int USER_INITIATED = 0x1000; public static final int APP_INITIATED = 0x2000; public static final int SERVER_INITIATED = 0x3000;
You can then tag your network requests accordingly:
Kotlin
TrafficStats.setThreadStatsTag(USER_INITIATED) TrafficStats.tagSocket(outputSocket) // Transfer data using socket TrafficStats.untagSocket(outputSocket)
Java
TrafficStats.setThreadStatsTag(USER_INITIATED); TrafficStats.tagSocket(outputSocket); // Transfer data using socket TrafficStats.untagSocket(outputSocket);
The HttpURLConnection
library automatically tags sockets based on the current
TrafficStats.getThreadStatsTag()
value. The library also tags and untags sockets when recycled through
keep-alive pools as shown in the following code sample:
Kotlin
class IdentifyTransferSpikeTask { @WorkerThread fun request(url: String) { TrafficStats.setThreadStatsTag(APP_INITIATED) // Make network request using HttpURLConnection.connect() ... TrafficStats.clearThreadStatsTag() } }
Java
public class IdentifyTransferSpikeTask { @WorkerThread public void request(String url) { TrafficStats.setThreadStatsTag(APP_INITIATED); // Make network request using HttpURLConnection.connect() ... TrafficStats.clearThreadStatsTag(); } }
Analyze network traffic types
When you look at the network traffic generated by your app, you need to understand the source of the traffic so you can optimize it appropriately. Frequent network activity generated by your app may be entirely appropriate if it is responding to user actions, but completely inappropriate if your app is not in the foreground or if the device is in a pocket or purse.
Analyze user-initiated traffic
User-initiated network traffic may be efficiently grouped together while a user is performing a specific task within your app, or spread out unevenly as the user requests additional information your app needs to get. Your goal in analyzing user-initiated network traffic is to look for patterns of frequent network use over time and attempt to decrease their frequency by grouping requests together.
The unpredictability of user requests makes it challenging to optimize this type of network use in your app. In addition, users expect fast responses when they are actively using an app, so delaying requests for efficiency can lead to poor user experiences. In general, you should prioritize a quick response to the user over efficient use of the network while a user is directly interacting with your app.
For recommendations to optimize user-initiated traffic, see Optimize user-initiated requests.
Analyze app-initiated traffic
App-initiated network traffic is typically an area where you can have a significant impact on the efficient use of network bandwidth. In analyzing the network activity of your app, look for periods of inactivity and determine if they can be increased. If you see patterns of consistent network access from your app, try to batch this traffic to allow the device radio to switch back into low-power mode between periods of activity.
For recommendations to optimize app-initiated traffic, see Optimize app-initiated requests.
Analyze server-initiated traffic
Network activity initiated by servers communicating with your app is also typically an area where you can have a significant impact on the efficient use of network bandwidth. Firebase Cloud Messaging (FCM) is a lightweight mechanism used to transmit data from a server to a particular app instance. Using FCM, your server can notify your app running on a particular device that there is new data available for it.
For recommendations to optimize server-initiated traffic, see Optimize server-initiated requests.
Use Battery Historian to visualize network traffic effects
Battery Historian is a tool that visualizes a device’s battery consumption over a period of time. You can use this tool to analyze how your network activity affects battery consumption. For example, Battery Historian can show you whether your app is using the cellular radio more frequently than you expect. For more information about using the Battery Historian, see Profile battery usage with Batterystats and Battery Historian.