The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.
Given that you are collecting metrics about bandwidth consumption, you can now start to determine ways to reduce that consumption. You may be able to permanently reduce that consumption (at least on a per-operation basis). You may be able to shunt that consumption to times or networks that the user prefers. This chapter reviews a variety of means of accomplishing these ends.
Understanding this chapter requires that you have read the core chapters and understand how Android apps are set up and operate, particularly the chapter on Internet access.
The best way to reduce bandwidth consumption is to consume less bandwidth.
(in other breaking news, water is wet)
In recent years, developers have been able to be relatively profligate in their use of bandwidth, pretty much assuming everyone has an unlimited high-speed Internet connection to their desktop or notebook and the desktop or Web apps in use on them. However, those of us who lived through the early days of the Internet remember far too well the challenges that dial-up modem accounts would present to users (and perhaps ourselves). Even today, as Web apps try to “scale to the Moon and back”, bandwidth savings becomes important not so much for the end user, but for the Web app host, so its own bandwidth is not swamped as its user base grows.
Fortunately, widespread development problems tend to bring rise to a variety of solutions — a variant on the “many eyes make bugs shallow” collaborative development phenomenon. Hence, there are any number of tried-and-true techniques for reducing bandwidth consumption that have had use in Web apps and elsewhere. Many of these are valid for native Android apps as well, and a few of them are profiled in the following sections.
Trying to get lots of data to fit on a narrow pipe — whether that pipe is on the user’s end or the provider’s end — has long been a struggle in Web development. Fortunately, there are a number of ways you can leverage HTTP intelligently to reduce your bandwidth consumption.
By default, HTTP requests and response are uncompressed. However, you can enable GZip encoding and thereby request that the server compress its response, which is then decompressed on the client. This trades off CPU for bandwidth savings and therefore needs to be done judiciously.
Enabling GZip compression is a two-step process:
Accept-Encoding: gzipheader to the HTTP request
Bear in mind that the Web server may or may not honor your GZip request, for whatever reason (e.g., response is too small to make it worthwhile).
Of course, avoiding a download offers near-100% compression. If you
are caching data, you can take advantage of HTTP headers to try to
skip downloads that are the same content as what you already have,
An HTTP response can contain either a
Last-Modified header or an
ETag header. The former will contain a timestamp and the latter
will contain some opaque value. You can store this information with
the cached copy of the data (e.g., in a database table). Later on,
when you want to ensure you have the latest version of that file,
your HTTP GET request can include an
If-Modified-Since header (with
the cached Last-Modified value) or an
If-None-Match header (with
ETag value). In either case, the server should return
304 response, indicating that your cached copy is up to
date, or a
200 response with the updated data. As a result, you
avoid the download entirely (other than HTTP headers) when you do not
need the updated data.
While XML and JSON are relatively easy for humans to read, that very characteristic means they tend to be bloated in terms of bandwidth consumption. There are a variety of tools, such as Google’s Protocol Buffers and Apache’s Thrift, that allow you to create and parse binary data structures in a cross-platform fashion. These might allow you to transfer the same data that you would in XML or JSON in less space. As a side benefit, parsing the binary responses is likely to be faster than parsing XML or JSON. Both of these tools involve the creation of an IDL-type file to describe the data structure, then offer code generators to create Java classes (or equivalents for other languages) that can read and write such structures, converting them into platform-neutral on-the-wire byte arrays as needed.
WebView, you should
consider standard tricks for compressing those scripts, collectively
referred to as
These techniques eliminate all unnecessary whitespace and such
from the files, rename variables to be short, and otherwise create a
syntactically-identical script that takes up a fraction of the space.
A chunk of the overhead involved in HTTP operations is simply establishing the socket connection with the Web server. Advertising that you want the socket to be kept alive, in anticipation of upcoming follow-on requests, can reduce this overhead.
Using higher-level HTTP clients, like OkHttp, helps here, because usually they handle all the details of keeping the socket open.
With SSL, though, keep-alive was not an option, until Google released the SPDY specification. SPDY in turn formed the basis of HTTP/2, the new standard for Web communications (replacing the venerable HTTP/1.1). OkHttp supports SPDY and HTTP/2.
Another way to consume less bandwidth is to only make the requests when it is needed. For example, if you are writing an email client, the way to use the least bandwidth is to download new messages only when they exist, rather than frequently polling for messages.
Off the cuff, this may seem counter-intuitive. After all, how can we know whether or not there are any messages if we are not polling for them?
The answer is to use a low-bandwidth push mechanism. The
quintessential example of this is GCM, the Google Cloud Messaging
system, available for Android 2.2 and newer. This service from Google
allows your application to subscribe to push notifications sent out
by your server. Those notifications are delivered asynchronously to
the device by way of Google’s own servers, using a long-lived socket
connection. All you do is register a
BroadcastReceiver to receive
the notifications and do something with them.
For example, Remember the Milk — a task management Web site and set of mobile apps — uses GCM to alert the device of task changes you make through the Web site. Rather than the Remember the Milk app having to constantly poll to see if tasks were added, changed, or deleted, the app simply waits for GCM events.
You could create your own push mechanism, perhaps using a WebSocket or MQTT. The downside is that you will need a service in memory all of the time to manage the socket and thread that monitors it. If you only need this while your service is in memory for other reasons, that is fine. However, keeping a service in memory 24x7 has its own set of issues, not the least of which is that users will tend to smack it down using a “task killer” or the Manage Services screen in the Settings app. Doze mode on Android 6.0+ will also cause problems with this approach.
A general rule of thumb is: don’t download it until you really need it.
Sometimes, you do not know if you really need a particular item until
something happens in the UI. Take a
ListView displaying thumbnails
of album covers for a music app. Assuming the album covers are not
stored locally, you will need to download them for display. However,
which covers you need varies based upon scrolling. Downloading a
high-resolution album cover that might get tossed in a matter of
milliseconds (after an expensive rescale to fit a thumbnail-sized
space) is a waste of bandwidth.
In this case, either the album covers are something you control on
the server side, or they are not. If they are, you can have the
server prepare thumbnails of the covers, stored at a spot that the
app can know about (e.g.,
.../cover.jpg it is
The app can then download thumbnails on the fly and only grab the
full-resolution cover if needed (e.g., user clicks on the album to
bring up a detail screen). If you do not control the album covers,
this option might still be available to you if you can run your own
server for the purposes of generating such thumbnails.
You can see a similar effect with the map tiles in Google Maps. When zooming out, the existing map tiles are scaled down, with placeholders (the gridlines) for the remaining spots, until the tiles for those spots are downloaded. When zooming in, the existing map tiles are scaled up with a slight blurring effect, to give the user some immediate feedback while the full set of more-detailed tiles is downloaded. And, if the user pans, you once again get placeholders while the tiles for the newly uncovered areas are downloaded. In this fashion, Google Maps is able to minimize bandwidth consumption by giving users partial results immediately and back-filling in the final results only when needed. This same sort of approach may be useful with your own imagery.
The preview of this section was traded for a bag of magic beans.
The preview of this section may contain nuts.
The preview of this section was fed to a gremlin, after midnight.