Tip:
Highlight text to annotate it
X
Hi.
My name is Reto Meier and I'm the tech lead for Google's
Android Developer Relations team.
In this Efficient Data Transfers DevByte, I'm going
to demonstrate a couple of quick techniques that you can
use to better understand when your application is making
network requests.
The aim is to modify the data transfer profile of your apps
to optimize for minimizing the battery drain caused by
activating the cell radio.
As you may have already seen in my DevByte on understanding
the cell radio, it's good practice to use the big cookie
approach to data transfers to avoid a fragmented network
traffic profile that keeps the cell radio powered up
indefinitely, in favor of minimizing the number of radio
state transitions.
And before you optimize your code, you need a way to
generate these graphs that show exactly when your app is
transferring data and to understand what you're looking
for so you can look for areas that need improvement.
There are several tools you can use for this.
Most simply, you can use logcat to generate log entries
that define the timing information of your network
requests at their start and end points, and then use the
exported log file to graph the output using the graphing tool
of your choice.
If you'd like someone to handle that graphing step for
you, AT&T offer the open source Application Resource
Optimizer, or ARO tool, that will generate the pretty
graphs and even overlay the battery state model, as well
as providing you with specific recommendations based on their
analysis of your app.
Another great alternative is to use the Network Statistics
tab in the DDMS view, which is part of the
Android Developer Tools.
As you can see in this screenshot, it displays the
amount of data being transferred and the direction
of the data flow.
Additionally, you can tag your output sockets to color-code
your data transfers, making it easier for you to see where
each transfer is originating.
If you're using the HTTP client to execute your
transfers, the ThreadStatsTag associated with the current
thread will automatically be associated with the socket
used for transfers using the HTTP client.
Note that for socket tagging to work, you need a device
running Jelly Bean or higher.
So once you've generated a representation of your app's
transfer profile, you can start analyzing it for battery
inefficiencies.
The most important thing to look for are
any periodic transfers.
Any regular pattern of repeating transfers will cause
a correspondingly regular pattern of radio activations,
so you're effectively setting the minimum
level of battery drain.
So the shorter the period between updates, the higher
the battery drain.
Now in this example, the period is 15 seconds, more
than enough to keep the battery powered up
indefinitely.
But depending on the device and carrier, even a period
over a minute may be enough to prevent the radio from ever
dropping into standby mode completely.
Now, we can incrementally reduce the battery pack by
lowering the frequency of these regular transfers.
But a better approach is to try and eliminate periodic
updates completely, allowing your app to have a baseline of
no battery drain.
Now, periodic data transfer spikes are typically an
indicator of either a regularly scheduled upload,
such as analytics or ad impressions, or client-side
scheduled polling, generally implemented using it a timer
or a repeating alarm.
You can eliminate both using some simple best practices.
Anything that's not time-sensitive, such as
analytics data, ad impressions, or anything else
that doesn't have a direct impact on the user experience
should be cued up and batched to be transferred
together as a group.
The best approach is to simply fill the queue and trigger all
these delay-tolerant downloads the next time you need to
perform something that is time-sensitive, like
downloading new music, updated headlines, current sports
scores, whatever data your users are interacting with.
If time-insensitive data is the only data you transfer,
you may find it necessary to schedule a daily sync.
In that scenario, the best approach is to use a
SyncAdapter to handle this for you.
We'll cover that in another DevByte.
But where a repeating alarm is the only thing that will work,
you can try and optimize that by randomizing the periodicity
as shown here and make the frequency as low as possible.
If you're polling your server to check for updates, you
should do everything in your power to replace client-side
polling with Google Cloud Messaging.
This technology lets you send messages from your server to
specific application instances through Google's Cloud
Messaging infrastructure.
So instead of having each device poll your server every
1, 5, 15, or 60 minutes to check for updates which may or
may not exist, you can have your server notify each device
when there's new information ready for it to download.
The result is an improved user experience thanks to both
better battery efficiency and lower latency updates.
I examine both SyncAdapters and Google Cloud Messaging in
more detail, as well as exploring some other
approaches to eliminating repeating transfers, later in
this DevByte series.
For now, having eliminated periodic transfers, let's look
at some more indicators for potential efficiency
improvements.
Another pattern to look for is short spikes, either in height
or duration.
While they may not be regular, they typically indicate
transfers that could be bundled together in the same
way as periodic transfers or possibly batched together or
even pre-fetched.
The same thing goes for transfers of any kind that
happen in close proximity but don't quite overlap.
Now, it's significantly more efficient to have transfers
happen when the radio is already in it's active state,
so when a time-sensitive transfer is initiated for
whatever reason you should look to preempt any transfers
which are likely to occur within the next few minutes.
This is particularly true of any scheduled transfers, but
also includes potentially user-initiated transfers.
Now for example, say you have a news app.
If you're forced to perform a download to let the user read
a full article that you haven't yet pre-fetched, you
should use this as an opportunity pre-fetch other
articles that they're likely to read next, particularly if
switching between your articles is as simple as
swiping left or right.
Now alternatively, if the action is time-sensitive but
not critical, for example uploading an image, it might
be worth holding off for up to 30 seconds, just in case
another transfer is initiated in the interim.
Now perhaps not surprisingly, this functionality is built
into the SyncAdapter, which also handles things like loss
connectivity and batching transfers across applications.
So it makes it a great approach for handling all of
your data transfers.
I'll dive into that and further efficient data
transfer topics in more detail in other DevBytes in this
series, including how to implement some of those best
practices using simple code patterns, Google Cloud
Messaging, and the SyncAdapter.