Reducing power consumption of connected apps

In this post I will cover a few important guidelines on how reduce power consumption of polling Android applications, i.e. applications that regularly connect to the internet, and get more out of the phone battery. I have crated a small sample app that puts all of the tricks into practice by setting up a background service that polls Twitter trends regularly and logs them to a file. By downloading the sample app and applying these guidelines, you will reduce the power consumption of your app by magnitudes, if you haven’t before. Click here to download the sample app.

We will cover four guidelines:

1. Synchronize you polls with other apps

2. Make polls short

3. Manage your connections

4. Stop your services

Synchronizing polls

Synchronizing you polls with other polling apps will reduce how often the radio is activated. It is achieved by using  AlarmManager with setInexactRepeat and a interval constant provided in AlarmManager, e.g. INTERVAL_HALF_HOUR.  These constants are special in that AlarmManager.setInexactRepeat will fire the intents at a regular intervals simultaneously, but not at exactly specified times. In the sample apps we have a BroadcastReceiver (Booter)  which wakes up at boot which in turn kick off a Service (Poller) at regular synched intervals:

public void onReceive(Context context, Intent rIntent) {
if (rIntent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Poller.class);
PendingIntent pIntent = PendingIntent.getService(context,0,intent, 0);
long interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
long firstPoll = System.currentTimeMillis();
am.setInexactRepeating(AlarmManager.RTC, firstPoll, interval, pIntent);
}
}

If apps don’t use this, this is what will happen when three different apps poll at random times within an interval:

As you can see, the radio has to stay in DCH and FACH state for a long time after each request and response transaction (user data) has been completed.  If they sync the polls (HTTP requests) it looks like this:

As you can tell, this will save lots of power for each poll cycle.

Make sure to use AlarmManager.RTC, not AlarmManager.RTC_WAKEUP, i.e. don’t wake up the application unless some other app is also being woken up. Google services run regularly and will make sure your app wakes up now and then.

Execute polls in as short time as possible

A poll with several requests and responses spread out over time will force the radio to stay in a high power state for long time. If a poll consists of multiple requests, fire them all before waiting for responses if they are independent. If not, then try to reduce the poll to as few requests as possible, e.g. by changing the server interface. If you don’t have control over the server, then use features like multiquery or batched queries, if supported (e.g. Facebook). If you control the server, make sure it’s response times are short (within a couple of seconds). The sample project only has a single request, so this tip is not illustrated, but is straightforward to implement.

Minimizing the amount of data sent and received saves battery. Thus, also make sure to set the Accept-Encoding HTTP header to “gzip”. Twitter and Facebook APIs both support gzip. The sample project defines two Interceptors and an Entity to add support for compression, which makes it transparent when using the client.

httpclient.addRequestInterceptor(new GzipHttpRequestInterceptor());
httpclient.addResponseInterceptor(new GzipHttpResponseInterceptor());
HttpGet httpget = new HttpGet("http://api.twitter.com/1/trends/current.jons");
HttpResponse response = httpclient.execute(httpget);

Manage HTTP connections

HttpURLConnection has a well known bug that leave connections hanging by not closing them as they should, when using HTTP GET. Specifically, it leaves connections hanging when using the Twitter API. After a minute or two after your requests is sent, the server forcibly closes the connection, causing a big power surge due to the radio link becoming active, even though your application is not sending or receiving any data. In the sample project I use the Apache client instead and make sure to close the connection forcibly using:

httpclient.getConnectionManager().shutdown();

Stop services

Services should be short lived and stopped as soon as they have finished their task, using stopSelf(). Have it wake up using intents when needed, e.g. using AlarmManager.

Sample app

In the sample app, the Poller class is only activated by the intent setup by the Booter class as you saw above. When an intent is fired, the Poller is activated by a call to onStart, which causes a poll to Twitter. After the poll, the Poller shuts down with a single line, causing it to not use any CPU until next intent fires it up.

Comments 13

Sort by: Newest | Oldest | Most popular

  1. By tony

    #1

    Hi, @hajons, thanks for sharing the tips, but it seems that the download link of the sample app is broken :), so will you please send a me a copy via email(tony_kwok[@]msn[dot]com), thanks a lot!

  2. By Dia Johnigan

    #3

    Great job for posting such a valuable website. Your web log is not only helpful but it is additionally very artistic too. There are often hardly any those who could create not so simple articles that artistically. Keep up the nice writing

  3. By Chris Jenkins

    #5

    Thanks for that, was searching for something like this for ages!

    Will make a blog post about my findings I shall be sure to mention you!

    Cheers,
    Chris

  4. By Zach Young

    #7

    Also on your poller service could you have used an IntentService instead?

  5. By Zach Young

    #8

    In your example, why do you use a PendingIntent?

  6. By Håkan Jonsson

    #9

    Hi Bachi,

    In the sample app, the CPU wakes up for a short time when the alarm triggers, which is enough for the poll to complete. If your service does something more time consuming you may need to use a partial wake lock to make sure the service completes its execution.

    However, in this and possibly most cases I see no danger in the device falling asleep while the service is executing. Polling is usually a non critical activity, since you don’t know what you get or even if you get anything, and wake locks should only be acquired when really needed. If you use them, make sure you release them properly. Failure to do so will cause battery to drain fast. The battery bug in the Facebook app released this summer was caused by a failure to release the wake lock: http://geekfor.me/news/facebook-1-3-wakelock/

    /Håkan

  7. By Bachi

    #10

    Thanks a lot for this tutorial and example app, it quite helped me!

    One question though: Should one not implement a wake lock to prevent the device from falling asleep while the service is executing?

    – Bachi

  8. By Håkan Jonsson

    #11

    Thanks for the feedback!

    I hope to be able to write an article on how to detect & troubleshoot power consumption problems related to networking in the future.

    /Håkan

  9. By Anders

    #12

    Very nice tutorial!
    Thanks for sharing.

    Cheers,
    Anders

  10. By Erik

    #13

    Thanks for the tutorial!

    Looking forward to more tips and tricks.

    Br
    Erik

1-13 of 13 comments.