Using Custom Events in Android Apps

There are two major concepts that provide a foundation for building scalable, native Android applications: loosely coupling requests and responses via events, and moving CPU intensive operations off the main thread. This post looks at events. It seems the online documentation on putting together all the pieces of  using custom events is sparse, at best. I’ll show you one example of how to put all the pieces together to create, dispatch and listen for custom events.  The end result is you will be able to decouple your application and make them more flexible and much less likely to break.

I’ve talked about event-based architectures before, and I think they are even more important when building applications for devices. They give you the ability to take into account processor delays and inconsistent internet connections. Between the three pieces of working with custom events described below you should be able to get up and running pretty quickly. I don’t go into much detail on what’s inside the various example as you can find those individual details by searching for them. It’s putting it all together that’s typically the hardest part.

The Event Class. Here’s an example of the content of an Event Class. I’ve taken this several steps further than most examples to include showing how to handle multiple Event types, which are accessed in this example as enums, and implementing multiple listeners. You can also extend your listeners with additional information of just about any type. In this example, I show using String’s for passing messages, but you could just as easily use custom Objects. This should give you a more realistic example of what goes into a commercial application.

	public class MapViewControllerEvent extends EventObject{

		private static final long serialVersionUID = 1L;

		public MapViewControllerEvent(Object source){
			super(source);
		}
	}

	public enum MapViewEvent{
		/**
		 * Connection to the internet has been lost.
		 */
		CONNECTION_LOST("Connection to the internet has been lost."),
		/**
		 * Connection to the internet has been restored.
		 */
		CONNECTION_RESTORED("Connection to the internet has been restored"),
		/**
		 * The LocationService has shutdown. Check logcat for errors.
		 */
		LOCATION_EXCEPTION("There was an unknown error related to the LocationService"),
		/**
		 * Indicates the LocationService is initialized and ready.
		 */
		LOCATION_INITIALIZED("The LocationService has been initialized. NOTE: it still may fail after a start() attempt."),

		private String description;
		private MapViewEvent(String description){
			this.description = description;
		}
	}

	public interface MapViewControllerEventListener extends EventListener{
		/**
		 * Indicates there has been a location change received from LocationService.
		 * @param event
		 * @param message
		 */
		public void onLocationChangeEvent(MapViewControllerEvent event,String message);
		/**
		 * Indicates whether or not device has internet connectivity.
		 * @param event
		 * @param message
		 */
		public void onConnectionChangeEvent(MapViewControllerEvent event,String message);
	}

	protected EventListenerList eventListenerList = new EventListenerList();

	/**
	 * Adds the eventListenerList for MapViewController
	 * @param listener
	 */
	public void addEventListener(MapViewControllerEventListener listener){
		eventListenerList.add(MapViewControllerEventListener.class, listener);
	}

	/**
	 * Removes the eventListenerList for MapViewController
	 * @param listener
	 */
	public void removeEventListener(MapViewControllerEventListener listener){
		eventListenerList.remove(MapViewControllerEventListener.class, listener);
	}

	/**
	 * Dispatches CONNECTION and LOCATION events
	 * @param event
	 * @param message
	 */
	public void dispatchEvent(MapViewControllerEvent event,String message){
		Object[] listeners = eventListenerList.getListenerList();
		Object eventObj = event.getSource();
		String eventName = eventObj.toString();
		for(int i=0; i<listeners.length;i+=2){
			if(listeners[i] == MapViewControllerEventListener.class){
				if(eventName.contains("CONNECTION"))
				{
					((MapViewControllerEventListener) listeners[i+1]).onConnectionChangeEvent(event, message);
				}
				if(eventName.contains("LOCATION")){
					((MapViewControllerEventListener) listeners[i+1]).onLocationChangeEvent(event, message);
				}
			}
		}
	}

Setup Listeners. Here’s how to set up the listener in your Activity. As an example, I called my Event Class MapViewController, but you can name it anything you like:

_mapViewController = new MapViewController(this);
_mapViewController.addEventListener(new MapViewControllerEventListener() {

	@Override
	public void onLocationChangeEvent(MapViewControllerEvent event,
			String message) {
		String eventName = event.getSource().toString();
		if(eventName.contains("EXCEPTION")){
			//TODO let user know
		}
		else{
			//TODO push UX change
		}

	}

	@Override
	public void onConnectionChangeEvent(MapViewControllerEvent event,
			String message) {
		// TODO Auto-generated method stub

	}
});

Dispatch Event. And, here’s how to dispatch an Event:

dispatchEvent(new MapViewControllerEvent(MapViewEvent.LOCATION_INITIALIZED),"Attempting to start location service.");

EventListenerList Class. For some reason, which I would characterize as a major oversite, Android does not currently provide a public EventListenerList Class. This Class makes creating custom events so much easier. However, the folks of the Firefly Client project for Android saved the day and were kind enough to create and post one publicly under an Apache License. The code is a bit dated, and shows some minor warnings when building Android v4, but it will work just fine. You’ll need to include a copy of this Class in your project to make things work.

So, that’s pretty much it. I hope this info helps you build better and more succesful projects.

References:

Java Tutorial – General Information about Writing Event Listeners

Firefly Client Android – EventListenerList Source Code

[Edited: June 7, 2012 – fixed various minor typos]

14 thoughts on “Using Custom Events in Android Apps”

  1. I got pretty much every thing but i am not able to figure out in which class will this part go..?

    protected EventListenerList eventListenerList = new EventListenerList();
     
    /**
     * Adds the eventListenerList for MapViewController
     * @param listener
     */
    public void addEventListener(MapViewControllerEventListener listener){
        eventListenerList.add(MapViewControllerEventListener.class, listener);
    }
     
    /**
     * Removes the eventListenerList for MapViewController
     * @param listener
     */
    public void removeEventListener(MapViewControllerEventListener listener){
        eventListenerList.remove(MapViewControllerEventListener.class, listener);
    }
     
    /**
     * Dispatches CONNECTION and LOCATION events
     * @param event
     * @param message
     */
    public void dispatchEvent(MapViewControllerEvent event,String message){
        Object[] listeners = eventListenerList.getListenerList();
        Object eventObj = event.getSource();
        String eventName = eventObj.toString();
        for(int i=0; i<listeners.length;i+=2){
            if(listeners[i] == MapViewControllerEventListener.class){
                if(eventName.contains("CONNECTION"))
                {
                    ((MapViewControllerEventListener) listeners[i+1]).onConnectionChangeEvent(event, message);
                }
                if(eventName.contains("LOCATION")){
                    ((MapViewControllerEventListener) listeners[i+1]).onLocationChangeEvent(event, message);
                }
            }
        }
    }

  2. @Harsh that block of code needs to be inside your custom event class along with all the other code above it in the example. The code block does three things. It lets you add and remove the event listener, and it exposes the public method for dispatching events. I hope that helps.

  3. Hello I am new to android development before working with Action Script 3.0, now I’m getting into mobile, if possible I would like to pass up the fonts files that example because I’m not succeeding.

  4. Is it possible to have a Listener registry class so that each class that fires the event need not implement the EventListener interface to handle it?
    The event handler function should be defined only in one class and the Registry class will point to it once an event is fired from any of the other classes.
    Eg A, B, C classes can fire the events but do not implement the Listener. Class X handles the events and implements the Listener.
    A,B,C and X all register with some sort of registry class.

    Also does the EventListenerList instance that we create store only our EventListeners or all the event listeners implemented in an Android Activity(Like OnInitListener etc)

  5. I have 3 activities A, B, C which want to fire events.
    I have a Listener class X which handles these events and implements the event listener interface created by me.

    A, B, C must not implement the eventlistener or know that it exists. The should not have to define the event handling method. They should only fire events which will then be handled by X.

    X should be able to get details(context, etc) of activities A, B, C and generate some sort of output(which will mostly be in the form of TTS) once an event is fired from any of A, B or C.

  6. First of all I am new to android and switched from AS2 so please ignore if I am having a childish question about android.

    I have a main activity A which call another java class J, from here I want to create an activity B using Context of main activity A but can’t start this activity for results as I can do it with Intent. So is it possible to communicate between class J and newly created activity B using some custom event handling?

    And secondly, does J will act like activity as it started from activity A or as an independent class.

  7. @antoinette That sounds exactly like Android Intents since they implement an abstract description. Do Intents not meet your needs for some reason?

  8. @abkniazi Yes, with two conditions: First, as long as class J implements are reference to the custom event Class it should receive events. And Second, class J must be currently instantiated. What I mean by that is Android destroys the previous Activity when it open a new Activity. So, class J must either be a singleton or it must be instantiated when Activity B is created. Make sense?

    As to your second question, Class J will be independent and not act like an Activity unless it sub-classes the Activity Class.

  9. @Agup Intents don’t really serve the purpose here as the only thing i want to be doing from any Activity is firing events. I managed to solve it myself though using a Controller class which defined implemented interface and defined the the handler function. Then by passing an instance of this class to the class that fired the events(in your case the class containing dispatchEvents) both the activity and controller class get registered.

  10. @antionette very cool, wish I could have help more. I’m curious and would be very interested to know more, are you able to briefly say a few words on the requirement driving your specific architecture?

  11. Hi Agup…

    Sorry, new to Android dev coming from AS3, C# and others.

    If I copy this code into a new class, I get many errors with this kind of message
    “The public type MapViewControllerEventListener must be defined in its own file”

    How should the code be implemented?

  12. @Mike check out the code in this github repo for a full working example of how to use the code above: https://github.com/Esri/quickstart-map-android/tree/master/EsriQuickStart/src/com/esri/quickstart. The code in this post is specifically for developers who are familiar and comfortable with AS3 custom events.

    Please note, I think Android broadcast events have matured enough that for many applications you should consider the pattern. Pay special note to the Security section here: http://developer.android.com/reference/android/content/BroadcastReceiver.html

    There is also a public interface listener pattern, that I personally think is by far the easiest to implement. Here is a good post that explains how this works http://www.vogella.com/articles/DesignPatternObserver/article.html

Comments are closed.