Offline App Architecture: how to build for the next billion

With all the hype around designing and developing Android apps for the next billion, it’s easy to get overwhelmed by the why, what and how of it. I will try to explain things by keeping it simple.

Why should I care?

What makes an app great is responsive user-experience. Users don’t care if the network is bad. They just see your app “freezing up” and they uninstall.

When you are on blazing fast LTE networks, everything seems fluid and there’s hardly any issue. But the next billion people are on low bandwidth data connections. The real challenge is to build great experience for these users.

Next Billion’s network connection

What should I do?

“Design your app to work offline, and it’ll work beautifully all the time.” – Joanna Smith

We want to build apps that work seamlessly on spotty networks. Say goodbye to the ugly error message when the user is offline.

How do I do that?

We’ve got a bunch of great tools at our disposal thanks to the Open Source community. So:

1. Determine connectivity

Use Facebook’s Network Connection class to listen to the traffic in the app and categorise the quality of the network. If you use OkHttp, add an interceptor to start sampling.

If you don’t use OkHttp, definitely check it out 🙂

2. Cache effectively

Know your enemy. The network is spotty and it’s your enemy. The disk is reliable and it’s your friend. Fetching data over the network is both slow and expensive. As a result, the ability to cache and reuse previously fetched resources is a critical aspect of optimizing for performance.

  • Each network resource can define its caching policy via the Cache-Control HTTP header.
  • Cache-Control directives control who can cache the response, under which conditions, and for how long.
  • The server uses the ETag HTTP header to communicate a validation token.
  • The validation token enables efficient resource update checks: no data is transferred if the resource has not changed.

3. Act locally, sync globally

Make the Model persistent. When there’s new data from the server, update your persistent Model and let your Presenter know to check with the Model and update the View. Use events/callbacks for communication.

“the View should always reflect what is in the persistent Model” — Yigit Boyar

Similarly when you want to post data to the server, update the persistent Model and let the View update itself, maybe to a transient state. Don’t show a progress dialog and wait for the server response to update the view. If the device is offline, retry whenever the device get’s back online. You never want your app to look like it forgot something or it’s stuck. Use something like Evernote’s Android Job to simplify job scheduling.

Finally, when the server responds, update the persistent Model and refresh the View as before. This way you can act locally, but sync globally.

4. Effective threading

Offload work to threads but avoid creating too many or too little threads. It’s important to have a separate queue for local tasks vs network tasks. You don’t want the network queue to get hung up and become a bottleneck for the local tasks.

Use RxJava and let the smart guys handle thread scheduling for you 😉

5. Optimize images

Use RGB_565 color profile, each pixel is stored on 2 bytes and only the RGB channels are encoded. Determine network type to control image quality. Re-fetch a higher quality image when the network is fast.

Slower network == more compression

6. Use the Big Cookie model

Understand the Android radio state machine. A fully active wireless radio consumes significant power, so it transitions between different energy states in order to conserve power when not in use. While the low and idle states drain significantly less battery, they also introduce significant latency every time you create a new network connection, as the radio transitions to the full power state.

A typical 3G wireless radio state machine, 2G is obviously worse.

With that in mind it’s important to bundle your data transfers in your network queue. Done correctly, you can ensure that the radio draws power for as short a duration as possible. You should batch your transfers by queuing delay tolerant transfers, and preempting scheduled updates and prefetches, so that they are all executed when time-sensitive transfers are required.

The underlying philosophy of this approach is to transfer as much data as possible(big cookie) during each transfer session in an effort to reduce creating small and frequent network connections(small cookies).

Let the legendary Reto Meier show you the details of Efficient Data Transfers.

TL;DR

  • Build for the user experience.
  • Have a persistent model.
  • Don’t think request/response. Act locally, sync globally.
  • Know your enemies(network), from your friends(disk).
  • Use the big cookie model.

If you liked this post, please hit the little heart! ❤

This post was inspired by Yigit Boyar’s Google I/O 2016 talk.
Check out Paresh Mayani’s slides for more tips.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s