page.title=Building for Billions
page.metaDescription=Best practices on how to optimize Android apps for low- and no-bandwidth and low-cost devices.
page.image=/distribute/images/billions-guidelines.png
@jd:body
Internet use—and smartphone penetration—is growing fastest in markets with
low, intermittent, or expensive connectivity. Successful apps in these
markets need to perform across a variety of speeds and devices, as well as
conserve and share information about battery and data consumption.
To help you address these important considerations, we’ve compiled the
following checklist. These do not follow a particular order, and as
always it's a good idea to research particularities of any market or country
you're targeting.
Over half of the users in the world still experience your app over 2G
connections. To improve their experience, optimize for no- and low-connection
speeds. For offline and slow connections: store data, queue requests, and handle
images for optimal performance.
Optimize images
- Serve WebP files over the
network. WebP reduces image load times, saves network bandwidth, and often
results in smaller file sizes than its PNG and JPG counterparts, with at
least the same image quality. Even at lossy settings, WebP can produce a
nearly identical image. Android has had lossy WebP support since
Android 4.0 (API level 14: Ice Cream Sandwich) and support for lossless /
transparent WebP since Android 4.2 (API level 17: Jelly Bean).
Dynamic image sizing
- Have your apps request images at the targeted rendering size, and have
your server provide those images to fit; the target rendering size will
vary based on device specifications. Doing this minimizes the network
overhead and reduces the amount of memory needed to hold each image,
resulting in improved performance and user satisfaction.
- Your user experience degrades when users are waiting for images to
download. Using appropriate image sizes helps to address these issues.
Consider making image size requests based on network type or network
quality; this size could be smaller than the target rendering size.
- Dynamic placeholders like
pre-computed palette values or low-resolution thumbnails can improve
the user experience while the image is being fetched.
Use image loading libraries
- Your app should not have to fetch any image more than once. Image
loading libraries such as Glide and Picasso
fetch the image, 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 the next time they are
requested.
- Image-loading libraries manage their cache, holding onto the most recent
images so that your app storage doesn’t grow indefinitely.
Optimize networking
Make your app usable offline
- In places like subways, planes, elevators, and parking garages, it is
common for devices to lose network connectivity. Creating a useful offline
state results in users being able to interact with the app at all times, by
presenting cached information. Ensure that your app is usable offline or
when network connectivity is poor by storing data locally, caching data,
and queuing outbound requests for when connectivity is restored.
- Where possible, apps should not notify users that connectivity has
been lost. It is only when the user performs an operation where connectivity
is essential that the user needs to be notified.
- When a device lacks connectivity, your app should batch up network
requests—on behalf of the user—that can be executed when
connectivity is restored. An example of this is an email client that allows
users to compose, send, read, move, and delete existing mails even when the
device is offline. These operations can be cached and executed when
connectivity is restored. In doing so, the app is able to provide a similar
user experience whether the device is online or offline.
Use GcmNetworkManager and/or Content Providers
- Ensure that your app stores all data on disk via a database or similar
structure so that it performs optimally regardless of network conditions
(for example, via SQLite + ContentProvider). The
GCM Network Manager
(
GcmNetworkManager
) can result in a robust mechanism to
sync data with servers while content
providers ({@link android.content.ContentProvider}) cache that data,
combining to provide an architecture that enables a useful offline state.
- Apps should cache content that is fetched from the network. Before making
subsequent requests, apps should display locally cached data. This ensures
that the app is functional regardless of whether the device is offline or
on a slow/unreliable network.
Deduplicate network requests
- An offline-first architecture 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
retrieval. This helps to ensure that network requests for the same piece of
data only occur once—the rest of the requests are satisfied locally. To
achieve this, use a local database for long-lived data (usually
{@link android.database.sqlite} or
{@link android.content.SharedPreferences}).
- An offline-first architecture always looks for data locally first, then
makes the request over the network. The response is cached and then returned
locally. Such an architecture simplifies an app’s flow between offline and
online states as one side fetches from the network to the cache, while the
other retrieves data from the cache to present to the user.
- For transitory data, use a bounded disk cache such as a
DiskLruCache
. Data that doesn’t typically change should only be requested once over
the network and cached for future use. Examples of such data are images and
non-temporal documents like news articles or social posts.
Fine-tune data transfer
Prioritize bandwidth
- Writers of apps should not assume that any network that the device is
connected to is long-lasting or reliable. For this reason, apps should
prioritize network requests to display the most useful information to the
user as soon as possible.
- Presenting users with visible and relevant information immediately is a
better user experience than making them wait for information that might not
be necessary. This reduces the time that the user has to wait and
increases the usefulness of the app on slow networks.
- To achieve this, sequence your network requests such that text is
fetched before rich media. Text requests tend to be smaller, compress
better, and hence transfer faster, meaning that your app can display useful
content quickly. For more information on managing network requests, visit
the Android training on Managing Network
Usage.
Use less bandwidth on slower connections
- The ability for your app to transfer data in a timely fashion is
dependent on the network connection. Detecting the quality of the network
and adjusting the way your app uses it can help provide an excellent user
experience.
- You can use the following methods to detect the underlying network
quality. Using the data from these methods, your app should tailor its use
of the network to continue to provide a timely response to user actions:
- {@link android.net.ConnectivityManager}>
{@link android.net.ConnectivityManager#isActiveNetworkMetered}
- {@link android.net.ConnectivityManager}>
{@link android.net.ConnectivityManager#getActiveNetworkInfo}
- {@link android.net.ConnectivityManager}>
{@link android.net.ConnectivityManager#getNetworkCapabilities}
- {@link android.telephony.TelephonyManager}>
{@link android.telephony.TelephonyManager#getNetworkType}
- On slower connections, consider downloading only lower-resolution media
or perhaps none at all. This ensures that your users are still able to use
the app on slow connections. Where you don’t have an image or the image is
still loading, you should always show a placeholder. You can create a
dynamic placeholder by using the
Palette library to generate placeholder colors that match the target
image.
- Prioritize network requests such that text is fetched before rich media.
Text requests tend to be smaller, compress better, and hence transfer
faster, meaning that your app can display useful content quickly. For more
information on adjusting bandwidth based on network connection, see the
Android training on Managing Network
Usage.
- On devices powered by Android 7.0 (API level 24) and higher,
users can turn on the
Data Saver setting, which helps minimize data usage. Android 7.0
extends {@link android.net.ConnectivityManager} to detect Data Saver
settings. For more information about this feature, see
Data Saver.
Detect network changes, then change app behavior
- Network quality is not static; it changes based on location, network
traffic, and local population density. Apps should detect changes in
network and adjust bandwidth accordingly. By doing so, your app can tailor
the user experience to the network quality. Detect network state using
these methods:
- {@link android.net.ConnectivityManager}>
{@link android.net.ConnectivityManager#getActiveNetworkInfo}
- {@link android.net.ConnectivityManager}>
{@link android.net.ConnectivityManager#getNetworkCapabilities}
- {@link android.telephony.TelephonyManager}>
{@link android.telephony.TelephonyManager#getDataState}
- As the network quality degrades, scale down the number and size of
requests. As the connection quality improves, you can scale up your
requests to optimal levels.
- On higher quality, unmetered networks, consider
prefetching data to make it available ahead of time. From a user
experience standpoint, this might mean that news reader apps only fetch
three articles at a time on 2G but fetch twenty articles at a time on
Wi-Fi. For more information on adjusting app behavior based on network changes,
visit the Android training on
Monitoring the Connectivity Status.
- The broadcast
CONNECTIVITY_CHANGE
is sent when a change in network
connectivity occurs. When your app is in the foreground, you can call
registerReceiver
to receive this broadcast. After receiving
the broadcast, you should reevaluate the current network state and adjust
your UI and network usage appropriately. You should not declare this receiver
in your manifest, as that feature is unavailable in Android 7.0 (API level 24)
and higher.
For more information about this and other changes in Android 7.0,
see
Android 7.0 Changes.
Related resources
Reaching new users means supporting an increasing variety of Android
platform versions and device specifications. Optimize for common RAM and
screen sizes and resolutions to improve the user experience.
Support varying screen sizes
Use density-independent pixels (dp)
- Defining layout dimensions with pixels is a problem because different
screens have different pixel densities, so the same number of pixels may
correspond to different physical sizes on different devices. The
density-independent pixel (dp) corresponds to the physical size of a pixel
at 160 dots per inch (mdpi density).
- Defining layouts with dp ensures that the physical size of your user
interface is consistent regardless of device. Visit the Android
guide on
Supporting Multiple Screens for best practices using
density-independent pixels.
Test graphics on ldpi/mdpi screen densities
- Ensure that your app layouts work well on low- and medium-density
(ldpi/mdpi) screens because these are
common densities, especially in lower-cost devices. Testing on
lower-density screens helps to validate that your layouts are legible on
lower-density screens.
- Lower-density screens can result in unclear text where the finer details
aren't visible. The Material Design guidelines describe
metrics and keylines to ensure that your layouts can scale across
screen densities.
- Devices with lower-density screens tend to have lower hardware
specifications. To ensure that your app performs well on these devices,
consider reducing or eliminating heavy loads, such as animations and
transitions. For more information on supporting different densities, see
the Android training on
Supporting Different Densities.
Test layouts on small/medium screen sizes
- Validate that your layouts scale down by testing on smaller screens. As
screen sizes shrink, be very selective about visible UI elements, because
there is limited space for them.
- Devices with smaller screens tend to have lower hardware specifications.
To ensure that your app performs well on these devices, try reducing or
eliminating heavy loads, such as animations or transitions. For more
information on supporting different screen sizes, see the Android
training on
Supporting Different Screen Sizes.
Backward compatibility
Set your targetSdkVersion and minSdkVersion
appropriately
- Apps should build and target a recent version of Android to ensure most
current behavior across a broad range of devices; this still provides
backward compatibility to older versions. Here are the best practices for
targeting API levels appropriately:
-
{@code targetSdkVersion} should be the latest version of Android.
Targeting the most recent version ensures that your app inherits newer
runtime behaviors when running newer versions of Android. Be sure to
test your app on newer Android versions when updating the
targetSdkVersion as it can affect app behavior.
-
{@code minSdkVersion} sets the minimum supported Android version.
Use Android 4.0 (API level 14: Ice Cream Sandwich) or Android 4.1 (API
level 16: Jelly Bean)—these versions give maximum coverage for modern
devices. Setting {@code minSdkVersion} also results in the Android build
tools reporting incorrect use of new APIs that might not be available in
older versions of the platform. By doing so, developers are protected
from inadvertently breaking backward compatibility.
- Consult the
Android dashboards, the Google Play Developer
Console for your app, and industry research in your target markets to
gauge which versions of Android to target, based on your target users.
Use the Android Support libraries
- Ensure your app provides a consistent experience across OS versions by
using the Google-provided support libraries such as AppCompat and the Design
Support Library. The Android Support Library package is a set of code
libraries that provides backward-compatible versions of Android framework
APIs as well as features that are only available through the library APIs.
- Some of the the highlights include:
- v4 & v7 support library: Many framework APIs for older versions of
Android such as {@link android.support.v4.view.ViewPager},
{@link android.app.ActionBar},
{@link android.support.v7.widget.RecyclerView}, and
{@link android.support.v7.graphics.Palette}.
- Design
Support library: APIs to support adding Material Design components
and patterns to your apps.
-
Multidex Support library: provides support for large apps that have
more than 65K methods. This can happen if your app is using many
libraries.
- For more information on the available support libraries, see the
Support Libraries Features section of the Android Developer site.
Use Google Play services
- Google Play services brings the best of Google APIs independent of
Android platform version. Consider using features from Google Play services
to offer the most streamlined Google experience on Android devices.
- Google Play services also include useful APIs such as
GcmNetworkManager
, which provides much of Android 5.0’s
{@link android.app.job.JobScheduler} API for older versions of Android.
- Updates to Google Play services are distributed automatically by the
Google Play Store, and new versions of the client library are delivered
through the Android SDK Manager.
Efficient memory usage
- Adjusting your memory footprint dynamically helps to ensure compatibility
across devices with different RAM configurations.
- Methods such as {@link android.app.ActivityManager#isLowRamDevice} and
{@link android.app.ActivityManager#getMemoryClass()} help determine memory
constraints at runtime. Based on this information, you can scale down your
memory usage. As an example, you can use lower resolution images on low memory
devices.
- For more information on managing your app’s memory, see the Android
training on Managing
Your App's Memory.
Avoid long-running processes
- Long-running processes stay resident in memory and can result in slowing
down the device. In most situations, your app should wake up for a given
event, process data, and shut down. You should use Google Cloud Messaging
(GCM) and/or
GcmNetworkManager
to avoid long running background
services and reduce memory pressure on the user’s device.
Benchmark memory usage
- Android Studio provides memory benchmarking and profiling tools, enabling
you to measure memory usage at run time. Benchmarking your app’s memory
footprint enables you to monitor memory usage over multiple versions of
the app. This can help catch unintentional memory footprint growth. These
tools can be used in the following ways:
- Use the Memory
Monitor tool to find out whether undesirable garbage collection (GC)
event patterns might be causing performance problems.
- Run Heap Viewer
to identify object types that get or stay allocated unexpectedly or
unnecessarily.
- Use
Allocation Tracker to identify where in your code the problem might
be.
- For more information on benchmarking memory usage, see the
Memory Profilers tools on the Android Developers site.
Related resources
Data plans in some countries can cost upwards of 10% of monthly income.
Conserve data and give control to optimize user experience. Reduce data
consumption and give users control over your app’s use of data.
Reduce app size
Reduce APK graphical asset size
- Graphical assets are often the largest contributor to the size of the
APK. Optimizing these can result in smaller downloads and thus faster
installation times for users.
- For graphical assets like icons, use Scalable Vector Graphics (SVG)
format. SVG images are relatively tiny in size and can be rendered at
runtime to any resolution. The Android Support
library provides a backward-compatible implementation for vector resources as
far back as Android 2.1 (API level 7). Get started with vectors with
this Medium post.
- For non-vector images, like photos, use WebP. WebP reduces
image load times, saves network bandwidth, and is proven to result in
smaller file sizes than its PNG and JPG counterparts, with at least the
same image quality. Even at lossy settings, WebP can produce a nearly
identical image. Android has had lossy WebP support since Android 4.0 (API
level 14: Ice Cream Sandwich) and support for lossless / transparent WebP
since Android 4.2 (API level 17: Jelly Bean).
- If you have many large images across multiple densities, consider
using Multiple
APK support to split your APK by density. This results in builds
targeted for specific densities, meaning users with low-density devices
won’t have to incur the penalty of unused high-density assets.
- For more information about reducing APK size, see
Reduce APK Size and
Shrink Your Code and Resources. In addition, you can
find a detailed guide on reducing APK size in this
series of Medium posts.
Reduce code size
- Be careful about using external libraries because not all libraries are
meant to be used in mobile apps. Ensure that the libraries your app is
using are optimized for mobile use.
- Every library in your Android project is adding potentially unused code
to your APK. There are also some libraries that aren’t designed with mobile
development in mind. These libraries can end up contributing to significant
APK bloat.
- Consider optimizing your compiled code using a tool such as ProGuard. ProGuard identifies
code that isn’t being used and removes it from your APK. Also
enable resource shrinking at build time by setting
minifyEnabled=true
, shrinkResources=true
in
build.gradle
—this automatically removes unused resources from
your APK.
- When using Google Play services, you should
selectively include only the necessary APIs into your APK.
- For more information on reducing code size in your APK, see the Android
training on how to Avoid
dependency injection frameworks.
Allow app to be moved to external (SD) storage
- Low-cost devices often come with little on-device storage. Users can
extend this with SD cards; however, apps need to explicitly declare that
they support being installed to external storage before users can move them.
- Allow your app to be installed to external storage using the
android:installLocation
flag in your AndroidManifest. For more
information on enabling your app to be moved to external storage, see the
Android guide on App Install
Location.
Reduce post-install app disk usage
- Keeping your app’s disk usage low means that users are less likely to
uninstall your app when the device is low on free space. When using caches,
it’s important to apply bounds around your caches—this prevents your app’s
disk usage from growing indefinitely. Be sure you put your cached data in
{@link android.content.Context#getCacheDir()}—the system can delete files
placed here as needed, so they won’t show up as storage committed to the
app.
Offer configurable network usage
Provide onboarding experiences for
subjective user choices
- Apps that allow users to reduce data usage are well received, even if
they demand heavy data requirements. If your app uses a considerable amount
of bandwidth (for example, video streaming apps), you can provide an
onboarding experience for users to configure network usage. For example,
you could allow the user to force lower-bitrate video streams on cellular
networks.
- Additional settings for users to control data syncing, prefetching, and
network usage behavior (for example, prefetch all starred news categories on
Wi-Fi only), also help users tailor your app’s behavior to their needs.
- For more information on managing network usage, see the Android training
on Managing
Network Usage.
Provide a network preferences
screen
- You can navigate to the app’s network settings from outside the app by
means of a network preferences screen. You can invoke this screen from
either the system settings screen or the system data usage screen.
- To provide a network preferences screen that users can access from within
your app as well as from the system settings, in your app include an
activity that supports the
{@link android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} action.
- For further information on adding a network preferences screen, see the
Android training on
Implementing a Preferences Activity.
Related resources
Access to reliable power supplies varies, and outages can disrupt planned
charges. Defend your users' batteries against unnecessary drain by benchmarking
your battery use, avoiding wakelocks, scheduling tasks, and monitoring sensor
requests.
Reduce battery consumption
- Your app should do minimal activity when in the background and when the
device is running on battery power.
- Sensors, like GPS, can also significantly drain your battery. For this
reason, we recommend that you use the
FusedLocationProvider
API. The
FusedLocationProvider
API manages the
underlying location technology and provides a simple API so that you can
specify requirements—like high accuracy or low power—at a high
level. It also optimizes the device's use of battery power by caching
locations and batching requests across apps. For more information about the
ideal ways to request location, see the Getting the Last
Known Location training guide.
- Wake
locks are mechanisms to keep devices on so that they can perform
background activities. Avoid using wake locks because they prevent the
device from going into low-power states.
- To reduce the number of device wake-ups, batch network activity. For more
information on batching, see the Android training on
Optimizing Downloads for Efficient Network Access.
-
GcmNetworkManager
schedules tasks and lets Google Play
services batch operations across the system. This greatly
simplifies the implementation of common patterns, such as waiting for
network connectivity, device charging state, retries, and backoff. Use
GcmNetworkManager
to perform non-essential background activity
when the device is charging and is connected to an unmetered network.
- For more information on how network activity can drain the battery, and
how to tackle this issue, see Reducing Network Battery Drain.
Benchmark battery usage
- Benchmarking your app’s usage in a controlled environment helps you
understand the battery-heavy tasks in your app. It is a good practice to
benchmark your app’s battery usage to gauge efficiency and track changes
over time.
-
Batterystats collects battery data about your apps, and
Battery Historian converts that data into an HTML visualization. For
more information on reducing battery usage, see the Android training on Optimizing
Battery Life.
Related resources
Make sure that your app works well on a variety of screens: offering good,
crisp graphics and appropriate layouts on low resolution and physically small
screens. Ensure that your app is designed to be easily localized by
accommodating the variations between languages: allow for spacing, density,
order, emphasis, and wording variations. Also make sure that date, time, and
the like are internationalized and displayed according to the phone’s
settings.
Fast and responsive UI
Touch feedback on all touchable items
- Touch feedback adds a tactile feeling to the user interface. You should
ensure your app provides touch feedback on all touchable elements to reduce
the perceived app latency as much as possible.
-
Responsive interaction encourages deeper exploration of an app by
creating timely, logical, and delightful screen reactions to user input.
Responsive interaction elevates an app from an information-delivery service
to an experience that communicates using multiple visual and tactile
responses.
- For more information, see the Android training on Customizing Touch
Feedback.
UI should always be interactive
- Apps that are unresponsive when performing background activity feel slow
and reduce user satisfaction. Ensure your app always has a responsive UI
regardless of any background activity. Achieve this by performing network
operations or any heavy-duty operations in a background thread—keep the UI
thread as idle as you can.
- Material Design apps use minimal visual changes when your app is loading
content by representing each operation with a single activity indicator.
Avoid blocking dialogs with
loading indicators.
- Empty
states occur when the regular content of a view can’t be shown. It might
be a list that has no items or a search that returns no results. Avoid
completely empty states. The most basic empty state displays a
non-interactive image and a text tagline. Where you don’t have an image, or
the image is still loading, you should always show either a static
placeholder, or create a dynamic placeholder by using the Palette
library to generate placeholder colors that match the target image.
- For more information, see the Android training on Keeping Your App
Responsive.
Target 60 frames per second on low-cost devices
- Ensure that your app always runs fast and smoothly, even on low-cost
devices.
- Overdraw can significantly slow down your app—it occurs when the pixels
are being drawn more than once per pass. An example of this is when you have
an image with a button placed on top of it. While some overdraw is
unavoidable, it should be minimized to ensure a smooth frame rate. Perform
Debug
GPU overdraw on your app to ensure it is minimized.
- Android devices refresh the screen at 60 frames per second (fps), meaning
your app has to update the screen within roughly 16 milliseconds. Profile
your app using on-device tools to see if and when your app is not
meeting this 16-ms average.
- Reduce or remove animations on low-cost devices to lessen the burden on
the device’s CPU and GPU. For more information, see the Android training on
Improving Layout
Performance.
- An efficient view hierarchy can speed up your app without increasing the
app's memory footprint. For more information, see
Performance
and View Hierarchies.
If anticipated start speed is low, use launch screen on first load
- The launch screen is a user’s first experience of your application.
Launching your app while displaying a blank canvas increases its perceived
loading time, so consider using a placeholder UI or a branded launch screen
to reduce the perceived loading time.
- A
placeholder UI is the most seamless launch transition, appropriate for
both app launches and in-app activity transitions.
-
Branded launch screens provide momentary brand exposure, freeing the UI
to focus on content.
- For more information on implementing splash screens, see the
Launch screens section of the Material Design spec.
- The best way to deal with slow start speeds is not to have them. Launch-Time Performance provides
information that may help you speed up your app's launch time.
UI best practices
-
Material Design is a visual language that synthesizes the classic
principles of good design with the innovation and possibility of technology
and science. Material Design aims to develop a single underlying system that
allows for a unified experience across platforms and device sizes. Consider
using key Material Design components so that users intuitively know how to
use your app.
- Ready-to-use Material Design components are available via the Design Support
library. These components are supported in Android 2.1 (API level 7) and
above.
Localization
- Your users could be from any part of the world and their first language
may not be yours. If you don’t present your app in a language that your
users can read, it is a missed opportunity. You should therefore
localize your app for key regional languages.
- To learn more, visit the Android training on
Supporting Different Languages.
- Starting from Android 7.0 (API level 24), the Android framework
makes available a subset of the ICU4J APIs, which can
help you localize your app into multiple languages. For more
information, see
ICU4J Android Framework APIs.
Related resources