You are here

You are here

Android's next billion will have poor connectivity, weak devices. Here's how to optimize

Aritra Roy Android Developer, Treebo
Android mascot tangled in ethernet cords

As Android developers, we all want our apps to reach a few hundred million users. But we mainly focus on the users in wealthy countries who have great Wi-Fi and cellular coverage and reasonably up-to-date devices. We completely forget about the growing number of users from developing countries who don't have great network infrastructure or technology—but may be using their mobile devices as their only computer.

In the next few years, the Android ecosystem is expected to grow by about 80% thanks to emerging markets. That's a huge opportunity for expanding our apps' user bases.

But with great opportunity come great challenges. You have to make sure that the users from these emerging markets using super-low-end devices on very poor network connections get a reasonably good user experience.

The quality of user experiences in these circumstances ultimately comes down to how you design your application, so today I'm going to show you some of the development strategies you can use to make your Android apps perform well in a variety of environments. That way, your apps will be ready for the next billion mobile users who come online.

Put your app on a strict diet

This is the first super-important thing that you need to look at if you want to tap into the massive emerging markets of Android users. In most developing countries, users don’t have access to fast, unmetered Internet connections. For them, every single megabyte of Internet data costs them, and if your app is too big for them to download, they will look for alternatives. You need to realize this bitter truth and try to make your apps smaller. Fortunately, it isn’t too difficult to do this. 

Use smaller libraries, or pieces of libraries

You can start by being a bit more selective when choosing third-party libraries. The best idea is to always check the methods count of the library you want to use before adding it to your project. If you need a small part (or feature) of the library, it is undeniably better to pull out those particular classes instead of dumping the entire library.

Optimize resource usage

You should start using Vector Drawables and spend some time optimizing the JPEG or PNG resources you are already using in your app. If you haven’t started using the new WebP feature in Android Studio 2.3, then you should start doing so immediately. 

Use WebP to significantly reduce image size

You should also consider reusing your resources intelligently, and programmatically create variants of them to reduce APK size. Some of the most effective tools for reducing APK size are ProGuard and DexGuard. In my opinion, you can't afford not to use one of these two tools.

You can also try the less common approach of on-demand asset loading. This means that instead of dumping all your assets (such as small video or audio files) directly into the app, you load them only when they are really needed.

Update less frequently

You should also be very careful while planning your app updates. I have seen many developers release very frequent app updates with minimal changes. It's true that continuous delivery is a good thing, but you also want each release to provide significant value to your customers. You should only push updates when you have made significant changes to the app. This way, users with data limits won't be wasting data every week just to install minor updates from your app.

Make your apps efficient

When you are making apps targeting the next billion users, you need to optimize them for memory, CPU, and battery. Not everyone uses high-end phones with 6 GB of RAM and an octa-core processor. In developing countries, cheap low-end devices with 512 MB of RAM and four-inch screens are not an exception at all.

Prevent memory leaks

You need to spend some time and effort in making your apps run smoothly even in the most low-end devices. One of the first things you need to look at while optimizing for memory is memory leaks. You can use this awesome library to quickly detect leaks in your app and fix them, which will prevent your app from consuming too much memory and crashing with the dreaded OutOfMemoryError.

Heed the threads

Another piece of advice is to pay close attention to concurrency on Android to ensure fast and smooth user experience. Try to get along with threads and make them your friend. You should always make a conscious effort to keep the main thread (or UI thread) of your app free for UI interactions only. Let the background threads handle everything else.

Animation downgrading

Mobile developers frequently use resource-heavy animations. They will run very well in newer, high-end devices, but low-end devices will struggle with them. One solution is to use this amazing library developed by Facebook to categorize devices on the basis of their hardware specifications and modify your app’s behavior on the basis of that.

For very low-end devices, you can disable all kinds of animations and make your app work well without them. For mid-end devices, you can disable the heavy animations and keep the lightweight ones turned on. And for high-end devices, you are free to do anything.

Start implementing adaptive content loading

This technique, if implemented properly, can massively improve the experience of your emerging-market users. Suppose you have an app that shows a list of images fetched from the Internet to your users. The user can scroll to load and view more images, but if the user is on a flaky or slow network connection (while traveling in a vehicle, for example, or using 2G or EDGE), then most of those large, high-quality images will not load and the user will either see a few images loaded here and there or a completely blank screen.

(The 500px app showing a list of high-quality images)

This is a very common problem in many apps, and it needs to be solved. To do this, use the adaptive content-loading technique to dynamically change the quality of the images you load depending on the quality of the network the user is on. This allows you to serve lower-quality images to users if they are on a slow or poor network connection. You can use an amazing utility library, Network Connection Class, to get an idea of the current network quality and fetch images based on the quality of the network.

There is a big difference between showing a lower-quality image to a user and showing nothing at all.

Comparison of several quality variants of the same image

Most importantly, your server needs to support this kind of mechanism. You need to keep several quality variants of a particular image (very low, low, medium, high, ultra-high, etc.) on your server and provide the one that will be best suited for a particular network connection. 

The bonus point here is that, if the network connection improves after some time, then you can re-fetch higher-quality images to replace the lower-quality ones. This way you can ensure that your user always gets the best possible experience even if the network connection quality changes frequently.

It’s time to make your app offline-first

I know that you might be thinking, “How can I make my app 100% offline?”

You can’t. Just as you can’t make the login or purchase flow in your app completely offline. At some point down the flow, you need the user to connect to the network to complete the action. But you can always make your app offline-first. This has been possible since 2013.

Don’t get confused between making 100% offline apps and making offline-first apps. It is quite sad that people tend to confuse these two and end up completely neglecting this super-important technique that has the potential to significantly improve the quality of your app.

Your primary focus should be to make your app do as much as possible while offline. It is not difficult, but you may need to completely change the approach you are currently using in your app to communicate with the network.

Suppose you have a photo-sharing app (such as Instagram), where people can like each other's photos. Using traditional methods, a user would click on the “like” button, and your app would make a network call and then wait for the response. If the call succeeds, you would update the view to let the user know that the “like” went through. Pretty simple, right?

It is simple, but it's not a good approach for many reasons. You should not block the user while you are making a network call from your app. Ideally, the user should not even know (in most situations) that your app is trying to talk to the Internet. It should be done seamlessly.

Liking a photo in the Instagram app

What if while browsing the app, the Internet connection drops for a few minutes, and in those few minutes the user taps the “like” button on a few photos? All those operations will fail. Now ask yourself, Is that a desirable user experience?

No, certainly not. You can completely change this scenario and make the UX much better by introducing a persistent data store that lives on the device or in the browser to make your app work seamlessly even when it is offline (SQLite, Realm, and IndexedDB are just a few choices). 

So from now on, whenever the user likes a photo, you update your model in the local storage and instantly reflect the change in the UI. You may or may not decide to make the network call right away. Even if you do so, you need to make sure it’s done silently and users don’t get blocked in their flow.

If the network connection is poor at that time, you can try to make the request sometime in the future or batch several “like” requests together in a single call later when your application logic deems fit (saving battery life and bandwidth).

It can eventually bring quite a lot of change into the architecture of your app, but it will be completely worth it. This will create a really amazing experience for your users, especially those on slow and intermittent network connections.

Use intelligent content prefetching

Here's a typical scenario.

Suppose you have developed a news reader app. You have a user who wakes up at 6 am and takes an 8 am bus to the office. It takes around an hour for the user to reach his office, and he wants to read all the latest news on the way while using your app.

Your app can do that, right? Yes, it absolutely can, but the question is how well it can do that. In most developing countries, it is quite common for the data connection to fluctuate a lot while traveling. Most places don’t even have proper network coverage. This can make it really difficult for users to browse news articles and enjoy the true experience of the app.

So how do you solve this problem and give users the best possible experience in critical situations such as this? The solution is not very difficult. You have to start making your app a bit more intelligent than it was before.

If you have the right tracking tools, you can find out that your user opens your app every day (weekdays to be precise) at 8 am and uses it for around an hour. So now you have the opportunity to prefetch some of the recent news articles every day at around 6 to 7 am, when the user is still at home and on an unmetered Wi-Fi connection.

So now, when the user opens up your app and tries to read some articles, the articles are already cached locally and just waiting to be read. Now, even if the network connection drops or gets terribly slow, the user doesn’t need to load most of the data, because it's already there in the phone's local database.

You can do this easily by scheduling a prefetching service using an AlarmManager or JobScheduler. But I recommend you use Evernote’s Android Job, which automatically switches between JobScheduler, AlarmManager, and GcmNetworkManager depending on the Android version of the user. It's super easy to use as well.

This technique can create a really smooth and amazing experience for your users, no matter what kind of network connection they are on.

Make a great experience, regardless of network quality

I hope that you will start implementing some (or maybe all) of these techniques in your apps to improve the experience for users who have low-end devices or poor-quality network connections in order to help your app grow successfully in emerging markets and work in remote regions.

If you liked this article, don’t forget to share and recommend it to fellow developers. If you know of any good tools or techniques for building efficient apps that function well, even in poor network environments, share those in the comments.

Image credit: Flickr

Keep learning

Read more articles about: App Dev & TestingApp Dev