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.
Connectivity
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 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 (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
android.database.sqlite
orSharedPreferences
). - 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:
- 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.
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:
- 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 callregisterReceiver
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 it will no longer function beginning with Android N. For more details see Android N behavior changes.
Related resources
Device Capability
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:
-
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. -
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. SettingminSdkVersion
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
ViewPager
,ActionBar
,RecyclerView
, andPalette
. - 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.
- v4 & v7 support library: Many framework APIs for older versions of
Android such as
- 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’sJobScheduler
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
Reduce memory footprint on low-cost devices
- Adjusting your memory footprint dynamically helps to ensure compatibility across devices with different RAM configurations.
- Methods such as
isLowRamDevice()
andgetMemoryClass()
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 Cost
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.
- A detailed guide on reducing your APK size can be found in 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
inbuild.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
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
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
Battery Consumption
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.
- 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. UseGcmNetworkManager
to perform non-essential background activity when the device is charging and is connected to an unmetered network. - Sensors, like GPS, can also have a significant drain on the battery. The recommended way to request location is to 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 on the ideal ways to request location, see the Getting the Last Known Location training guide.
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
Content
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.
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.
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.