4.3: Best practices: network, battery, compression

Contents:

Most users are on limited bandwidth with expensive data plans, and all users are unhappy when an app drains their battery. Networking is one of the biggest users of battery. To reduce battery drain and reduce the size and frequency of data transfers, follow best practices to optimize your networking.

Over half of users worldwide will experience your app over a 2G connection. Improve their experience by optimizing your app for low-speed connections and offline work. To do this, store data, queue requests, and handle images for optimal performance.

The general aim for developers is to:

  • Reduce active radio time. You can do this by optimizing the frequency of network requests, and by batching requests.
  • Reduce size of data per request.

Optimizing network and battery performance is a huge topic, and as devices and the Android OS change, so do some of the recommendations. The Android team constantly improves the framework and APIs to make it easier for you to write apps that perform well. To dive deeper and get the most up-to-date recommendations, make use of the extensive resources to which this document links.

This chapter presents a basic introduction to network and battery optimization, along with recommended practices.

Radios

Inside your mobile device is a small piece of hardware that's essentially a radio . The purpose of this radio is to communicate with local cell towers and transmit data. However, this radio is not always active and drawing power. It starts in a powered down state, and when the device needs to send data, the radio turns on and transfers the data. After the data packet is sent, the radio stays awake for a while, in case the server sends a response. Eventually, the radio goes back to sleep to save battery.

IMPORTANT: The exact number of states and amounts of power drawn depend on the device and the Android version. The radio state machine on each device varies based on the mobile radio technology employed—2G, 3G, LTE, etc. This variation is particularly true of the transition delay ("tail time") and startup latency. The device's carrier network defines and configures these attributes.

The state machine for a typical 3G network radio consists of three energy states:

  1. Full power: Used when a connection is active, allowing the device to transfer data at its highest possible rate.
  2. Low power: An intermediate state that uses around 50% of the battery power of the full-power state.
  3. Standby: The minimal energy state during which no network connection is active or required.

While the low and standby states drain a lot less battery than the full-power state, they also introduce significant latency to network requests. Moving from low power to full power 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. The diagram below uses AT&T's timings for a typical 3G radio. The diagram shows how long it takes the radio to switch between standby, a low-power state, and a full-power state. The arrows show the direction of state changes and the latency in seconds between state changes.  Radio states diagram showing how long it takes to switch between standby, low-power state, and a full-power state. The arrows show the direction of state changes and the latency in seconds between state changes.

For more about Android radio states, see The Radio State Machine.

From the perspective of sending a request and receiving a response:

  • There is a high drain on the battery when the radio first wakes up, as shown in yellow in the image below (Wakeup hardware).
  • There are some additional spikes when data packets are sent and received, as shown in blue and purple in the image (Packet Sent and Packet Received).
  • There is a continuous draw when the radio is in the low-power state or the standby state, as shown in red in the image (Keep awake).  Cell radio energy states simplified: <strong>Wakeup hardware</strong> (hardware wakes up), <strong>Packet Sent</strong> (a data packet has been sent), <strong>Packet Received</strong> (a data packet has been received), and <strong>Keep awake</strong> (radio stays awake, in case there are more packets soon).

When the cellular radio starts, the radio hardware wakes up, and there is an associated battery cost. After a package is sent, the radio stays awake for another 20-30 seconds, waiting for server responses. Then the radio powers into a lower state, and then it turns off.

You need to minimize the number of times the radio is powered up. Send as much data as you can per cycle, and get a server response before the hardware is turned off. Doing all this requires some timing acrobatics, and there are several APIs, including JobScheduler and FirebaseJobDispatcher, to help you. Even using these APIs, you should follow network and battery best practices.

Network and battery best practices

Sending and receiving data packets over the network is one of the biggest users of battery. Your biggest gain in battery efficiency may come from improving how your networking interacts with the device's hardware.

There are three types of networking requests, and they need to be handled differently:

  • Do now. These networking requests are mostly user-initiated, and the user expects a fast response. As such, there is not much to optimize here. Signing in and requesting a photo are examples of "do now" networking requests.
  • Server response. The app makes a request to the server, and the server sends a response. To improve resource usage for the request and reduce the size of the request and response, ask only for the data you need, and use compression.
  • Data push such as uploading analytics, saving state, or syncing. You can optimize the timing of the push and the size of the received data.

Best practices for reducing network usage and battery consumption:

  • Never poll the server for updates. An app that pings the server every 20 seconds, just to acknowledge that the app is running and visible to the user, keeps the radio powered on indefinitely. Keeping the radio on for this reason results in a significant battery cost for almost no actual data transfer. Instead, use a service such as Firebase Cloud Messaging, which allows the server to let your app know when new data is available.
  • Do not oversync. Sync data only as often as you must. And ideally, sync when the phone is on Wi-Fi and plugged in.
  • Use an (exponential) back-off pattern when you sync or poll. That is, extend the time between subsequent polls, as described in Exponential backoff. A service like Firebase Cloud Messaging will handle this task for your app.
  • Bundle requests. Instead of sending one request at a time, bundle and send multiple requests together. This optimizes the way your app takes advantage of an active cell radio. Use the JobScheduler API or theFirebase JobDispatcher API. For more about bundling requests, see Batch Transfers and Connections.

    The diagram below shows relative mobile radio power use for unbundled data transfers (on the left) versus bundled data transfers (on the right). Red sections in the pie graphs indicate high power use, orange sections indicate low power use, and blue indicates standby state.  Relative mobile radio power use for <strong>Unbundled Transfers</strong> (left) versus <strong>Bundled Transfers</strong> (right). Red sections indicate high power use, orange sections indicate low power use, and the blue section indicates standby state.

  • Defer non-immediate requests until the user is on Wi-Fi or the phone is plugged in. The Wi-Fi radio uses significantly less battery than the mobile radio. See also Modifying your Download Patterns Based on the Connectivity Type.

  • Prefetch data. Try to predict what the user might need in the next few minutes and download it ahead of time along with requested data. This requires some amount of guessing, but if you predict correctly, you save. For example, if the user is listening to a playlist, it is likely they will listen to the next song after this one, and you can prefetch it. And for a news app, it might make sense to prefetch all the breaking news, or to prefetch the publications that the user prefers. See Prefetch Data.
  • Adapt to what the user is doing. For example, if the user's phone has been inactive for 8 hours and suddenly becomes active, and it is morning, it is likely that the user just woke up. If the device is on Wi-Fi, sync the user's email while the device is still on Wi-Fi, instead of when the device is off Wi-Fi during the user's commute to work or school.
  • Fetch text before media. Text requests tend to be smaller and compress better than media requests. That means text requests transfer faster, which means that your app can display useful content quickly. Use "lazy" loading of images. Cancel the image download if the user scrolls past the item.
  • Compress your data. In general, it takes much less battery power for the CPU to compress and decompress data than it takes for the radio to transfer that same data over the network uncompressed. For more on image compression, see the Optimizing images section of this document. For a list of resources on data compression, see the Learn more section of this document.

Make your apps usable offline:

  • Bundle network requests when users are offline. For example, let users compose messages when offline and send the mesages when online.
  • Store and cache data locally. Caching is turned off by default for Android apps. To enable caching of all responses, use the HttpResponseCache class. (For details, see the #CACHEMATTERS for networking video.)

    Use Firebase Cloud Messaging in combination with local storage. You can use a database or similar structure so that it performs optimally regardless of network conditions, for example by using SQLite with a content provider).

  • Consider an offline-first architecture, which initially tries to fetch data from local storage and, failing that, requests the data from the network. After being retrieved from the network, the data is cached locally for future use. This helps to ensure that network requests for the same piece of data only occur once—with subsequent requests satisfied locally. To achieve this, use a local database for long-lived data (usually SQLite or SharedPreferences).

  • The best way to save on networking performance is not to download or upload data at all. Apps should cache content that is fetched from the network and likely to be used again. Before making subsequent requests, apps should display locally cached data. This ensures that the app is functional even if the device is offline or on a slow or unreliable network. See also ETag and HTTP 304 for conditional requests.

Adapt to available connectivity and quality

Use the following methods to detect network status and capabilities. Use the data from these methods to tailor your app's use of the network so that your app provides timely responses to user actions:

Follow these practices:

  • On slower connections, consider downloading only lower-resolution media, or perhaps no media at all.
  • Adapt your app's behavior by checking for connectivity and network capabilities before making requests or sending data.
  • If your app performs a lot of network operations, provide user settings that allow users to control your app's data habits. For example, give users control over how often your app syncs data, whether to perform uploads and downloads only when on Wi-Fi, whether to use data while roaming, etc.
  • Fetch large data only if connected to a Wi-Fi network.
  • Prefetch data when device is connected to Wi-Fi.

The following code snippet tests network connectivity for Wi-Fi and mobile. The code determines whether Wi-Fi or mobile network interfaces are available (that is, whether network connectivity is possible) and connected. That is, the code tests whether network connectivity exists, and whether it is possible to establish sockets and pass data.

You need the ACCESS_NETWORK_STATE permission to read info about a device's network connection. You need INTERNET permissions to connect to the internet or network.

ConnectivityManager cm =  
    (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && 
                      activeNetwork.isConnectedOrConnecting();
int connectionType = -1;
if(isConnected) {
    connectionType = activeNetwork.getType();
    if (connectionType == ConnectivityManager.TYPE_WIFI) {
        Log.d(DEBUG_TAG, "Mobile connected: to Wi-Fi");
    } else if (connectionType == ConnectivityManager.TYPE_MOBILE) {
        Log.d(DEBUG_TAG, "Mobile connected: to Cellular Network");
    }
}
else{
    Log.d(DEBUG_TAG, "Mobile connected: No active network connection");
}

See more at Managing Network Usage.

Optimizing images

Most downloaded traffic in an app consists of images. As a result, the smaller you can make your downloadable images, the better the network experience that your app can provide for users. This section provides guidance on making image files smaller and more network-friendly.

There are a number of ways to make images faster to download. These include serving WebP images, dynamically sizing images, and using image-loading libraries.

Images for Android apps are typically JPG, PNG, or WebP format. If you support WebP, use WebP.

WebP images

WebP is an image file format from Google. WebP provides lossy compression (as JPG does) and transparency (as PNG does), but WebP can provide better compression than either JPG or PNG.

Serve WebP files over the network to reduce image load times and save network bandwidth. A WebP file is often smaller in size than its PNG and JPG counterparts, with at least the same image quality. Even using lossy settings, WebP can produce a nearly identical image to the original. Android has included lossy WebP support since Android 4.0 (API 14) and support for lossless, transparent WebP since Android 4.2 (API 18).

PNG images

Optimize PNG images by reducing the number of unique colors. This is a preprocessing step that you apply to your images, and there are tools to help you. See Reducing Image Download Sizes.

In the image below, before reducing the number of unique colors (left) and after (right), you can see that there's a loss of quality. Most of the gradient colors have been replaced, imparting a banding effect to the image. Zoom in on the image to see the data loss even more clearly.  Before reducing the number of unique colors (left) and after (right), you can see that there's a loss of quality. Most of the gradient colors have been replaced, imparting a banding effect to the image. Zoom in on the image to see the data loss even more clearly.

JPG images

Adjust the quality of the image to the minimum required. For an average JPG there is a very minor, mostly insignificant change in apparent quality from 100 to 75, but a significant file size difference for each step down. This means that many images look good to the casual viewer at quality 75, but the images are half as large as they would be at quality 95. As quality drops below 75, there are larger apparent visual changes and reduced savings in file size.

Choose the right image format

Different image formats are suitable for different types of images. JPG and PNG have very different compression processes, a nd they produce quite different results.

The decision between PNG and JPG often comes down to the complexity of the image itself. The figure below shows two images that come out quite differently depending on which compression scheme you use. The image on the left has many small details, and thus compresses more efficiently with JPG. The image on the right, with runs of the same color, compresses more efficiently with PNG.  Suitable cases for JPG versus PNG

WebP as a format can support both lossy and lossless modes, making it an ideal replacement for both PNG and JPG. The only thing to keep in mind is that it only has native support on devices running Android 4.2.1 (API level 17) and higher. Fortunately, the large majority of devices satisfy that requirement.

Use the following algorithm to decide:

Do you support WebP? 
        Yes: Use WebP
        No: Does it need transparency?
                Yes: Use PNG
                No: Is the image "simple" (colors, structure, subject?)
                       Yes: Use PNG
                        No: Use JPG

Determine optimal quality values

There are several techniques you can use to achieve the right balance between compression and image quality. The technique described below uses scalar values, and therefore only works for JPG and WebP. Another technique takes advantage of the Butteraugli library, which estimates the psychovisual difference between two images (that is, it estimates the difference in how the user perceives the two images). The Butteraugli library is usable for all image formats.

Use scalar values (JPG and WebP only)

The power of JPG and WebP comes from the fact that you can use a scalar value between 0 and 100 to balance quality against file size. The trick is finding out what the correct quality value is for your image. Too low a quality level produces a small file at the cost of image quality. Too high a quality level increases file size without providing a noticeable benefit to the user.

The most straightforward solution is to pick some non-maximum value, and use that value. However, be aware that the quality value affects every image differently. While a quality of 75%, for example, may look fine on most images, there may be some cases that do not fare as well. You should make sure to test your chosen maximum value against a representative sample of images. Also, make sure to perform all of your tests against the original images, and not on compressed versions.

For large media apps that upload and re-send millions of JPGs a day, hand-tuning for each asset is impractical. You might address this challenge by specifying several different quality levels, according to image category. For example, you might use 35% as the quality setting for thumbnails, since a smaller image hides more compression artifacts.

Serve sizes

It is tempting to keep only a single resolution of an image on a server. When a device accesses the image, the server serves the image at that one resolution and leaves downscaling to the device.

This solution is convenient for you, but it might force the user to download more data than they need. Instead, store multiple sizes of each image and serve the size that is most appropriate for each use case. For example, for a thumbnail, serve an actual thumbnail image instead of serving a full-sized version of the image that is downscaled on the device.

Serving sized images is good for download speed, and it's less costly for users with limited or metered data plans. When you serve sized images, the images take less space on the device and in main memory. For large images, such as images with 4K resolution, this approach also saves the device from having to resize images before loading them.

To implement this approach, you need a backend image service that can provide images at various resolutions with proper caching. Services exist that can help with this task. For example, Google App Engine comes with image-resizing functionality already installed.

For more about image formats and how to choose the right ones, see Reducing Image Download Sizes.

Dynamically resize images

When you download images from a server, request the size appropriate for the device. Adjust image size and quality based on the network bandwidth and quality. Dynamically resizing images in this way reduces transferred data size, improves download speed, and reduces the amount of memory needed on the device.

Use image loading libraries

Your app should not download any image more than once. Image loading libraries such as Glide and Picasso fetch the image once, cache it, and provide hooks into your views to show placeholder images until the actual images are ready. Because images are cached, these libraries return the local copy of the image the next time an image is requested.

Text data compression

In addition to optimizing images, you may be able to reduce the size of text-heavy pages. Start by testing how long a page takes to load on a mobile device, and if it is a problem, consider these remedies:

  • Make the page smaller. Edit the page to use fewer words. Remove content that is not relevant. Break the page up into smaller pages.
  • Minify. CSS and Javascript minifiers are powerful, easy to use, and fit into existing build pipelines.
  • Compress. Ensure that your server has GZIP compression turned on. Generate better compressed GZIP data offline using Zopfli or 7-zip.

See Text Compression for Web Developers.

Serializing data

Serialization is the process of converting structured data into a format that can be stored and sent over the network. At the destination, the original data is reconstructed. JSON and XML are example serialization formats that are human-readable, but bulky and slow.

Use FlatBuffers to create a schema that describes your data, then compile the data into source code that can serialize and deserialize your data. It's smaller, faster, and less painful than using JSON or XML.

See the Serialization with Flatbuffers video and the FlatBuffers documentation.

The related exercises and practical documentation is in Optimizing network, battery, and image use.

Learn more

Network:

Battery:

WebP:

Compression:

Flatbuffers:

results matching ""

    No results matching ""