Monitoring Android Events

Use dynamically registered BroadcastReceivers to create intelligent GUIS for Android

Smart GUI

In past articles our Android sample application GUIs have consisted of a simple button or two and the corresponding click-handler code.

What if some of those buttons are only supposed to be available under specific device conditions? Examples include providing a button only when a connection to a remote Bluetooth peripheral has been established or some other specific device condition.

Or let’s say your device is going to be used to compute pi to the … some really high precision value that is likely to take a lot of computational cycles. You might not want to perform that calculation if your device is not connected to power in the interest of battery life.

Or you may only want your device’s special features to be enabled when the device is “docked”.

The “easy solution” is to check, or poll, for this condition periodically in addition to checking at application start-up or even based on various user interaction. While this approach will generally work, the preferred approach is to let our applications be “event-driven”. Then the user interface may be updated as soon as the condition changes — without the reliance upon some sort of user interaction, or costly polling.

Performing constant polling of device conditions is poor programming practice as it can cause numerous problems with the application performance, user experience and battery life.

So, if polling is bad news, what are we to do?

Handling Events

The Android platform permits applications to “register” for various events of interest.

By registering for an event, we can free the application from polling or other tedious programming practices. The recipe is fairly simple:

  • Create a BroadcastReceiver
  • Register for an event of interest
  • Handle the event accordingly when it occurs

There are two basic approaches to handling events in Android.

The first approach is to implement a BroadcastReceiver as a stand-alone class. A BroadcastReceiver is one of the fundamental application types on the Android platform, along with Activity, Service and ContentProvider.

A BroadcastReceiver is typically implemented as a separate class and defined in the AndroidManifest.xml file with the <receiver> tag. The receiver has associated with it one or more IntentFilter instances. The IntentFilter declaration tells the operating system that this particular class is interested in notification when a particular event occurs. When the event occurs a method implemented in the BroadcastReceiver class named onReceive() is invoked. This is the basic approach used by applications to handle alarms and other asynchronous triggers as seen in a prior article which discussed launching Android applications.

The challenge with a distinct BroadcastReceiver is that this code is not always easy to integrate with your user interface code. If you want to have a tight integration between received events and the user interface there is another approach worth considering: dynamic registration of a newly created BroadcastReceiver from within an Activity. This article demonstrates this latter approach.

Demonstration code

In the balance of this article we construct a simple application which displays a different graphic on the screen depending on the presence of a power connection to the phone.

While not a heart-stopping, exciting application, it is enough functionality to demonstrate a very useful programming technique for Android. Let’s get started.

The application consists of a single Activity with a user interface defined in main.xml. The UI layout has a single view which is centered on the screen. At runtime we update the view’s background to have a different image, or as it is known in the Android world, a “drawable”.

The image below shows the project in Eclipse. Note the two files under the /res/drawable folder. These images are stored in the drawables folder because it wasn’t worth the trouble to make high, low and medium resolution versions of the images which would ordinarily be added to the drawable-hdpi, drawable-ldpi, and drawable-mdpi respectively.

Project setup in Eclipse
Project setup in Eclipse

Let’s look at the layout file to see our simple user interface. We have a single View instance within an encompassing LinearLayout. When the code runs this view’s background image is changed to match the detected events.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    >
<View
    android:layout_width="64px"
    android:layout_height="64px"
    android:id="@+id/powermeter"
    />
</LinearLayout>

The next listing shows the code for the application, which is discussed below the code listing.

package com.msi.linuxmagazine.lookforpower;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;

public class LookForPower extends Activity {

	private BroadcastReceiver powerMonitor = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // create an instance of a BroadcastReceiver
        powerMonitor = new BroadcastReceiver() {
        	public void onReceive(Context context,Intent intent) {
        		if (intent.getAction().equals("android.intent.action.ACTION_POWER_CONNECTED")) {
        			View v = findViewById(R.id.powermeter);
        			if (v != null) {
        				v.setBackgroundResource(R.drawable.green);
        			}
        		}
        		if (intent.getAction().equals("android.intent.action.ACTION_POWER_DISCONNECTED")) {
        			View v = findViewById(R.id.powermeter);
        			if (v != null) {
        				v.setBackgroundResource(R.drawable.red);
        			}
        		}
        	}
        };
    }
    @Override
    public void onResume()
    {
    	super.onResume();
    	// Register interest for two different actions related to power
    	registerReceiver(powerMonitor,new IntentFilter("android.intent.action.ACTION_POWER_CONNECTED"));
    	registerReceiver(powerMonitor,new IntentFilter("android.intent.action.ACTION_POWER_DISCONNECTED"));
    }
    @Override
    public void onPause()
    {
    	super.onPause();
    	// unregister
    	unregisterReceiver(powerMonitor);
    }

Some things to note in this code:

  • We import a handful of classes from the android.content package including:
    • BroadcastReceiver — this is the class that allows us to be “called back” when an event occurs
    • Context — used all over the Android platform for providing… you guessed it: context for the running state of the application
    • Intent — an Intent is passed to the BroadcastReceiver to indicate that a particular event has occurred
    • IntentFilter — the IntentFilter is used to register for a particular action of interest
  • We dynamically create a BroadcastReceiver including its onReceive method
  • In the onReceive method we check for two specific Intent actions, both related to a power connection
  • The onResume and onPause Activity life-cycle methods are crucial for registering and unregistering specific IntentFilters
  • The code to update the view’s background is a simple call to setBackgroundResource()

Testng the application is simple: run the application and at first the screen will be blank. Why? Because no events have been captured yet.

Application startup.  Very boring!
Application startup. Very boring!

When the device is connected to power an Intent is passed to our powerMonitor BroadcastReceiver’s onReceive method and the green image is set as the background of the view.

Power connected.
Power connected.

Take the power away, i.e. unplug the USB connector and the green image is replaced with the red image because the POWER_DISCONNECTED event was detected.

Power disconnected
Power disconnected

That is about as simple as it gets for in-line, in-Activity Intent monitoring. There are a number of events which can be trapped — have a look at the Intent class definition on the Android developer site for a list of the different ACTIONs which may be observed.

That’s all for this installment. Please contact me at fableson-at-msiservices-dot-com and let me know what Android, iPhone or other mobile development topics you would like to see in future articles.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62