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]

Simple Native JavaScript Classes – So simple a caveman could do it.

Among the many things I severely dislike about JavaScript is working with applications that have oodles of global variables, multiple .js libraries and dozens of loosely organized, individual functions. This is a very common pattern (or perhaps an anti-pattern). But it’s a terrible way to code for medium to large projects, especially where you have to share your code. Here’s a highly simplified example:


var someNumber = 2; //Global variable

function add(number){
    return someNumber + number;
};
alert(add(4)); //displays "6"

It’s unfortunate that this pattern is reinforced in authoritative books, blogs and articles. It’s completely pervasive in the majority of examples you see on the web. The downside is modifying, testing and troubleshooting this pattern can be an absolute nightmare. I compare it to building a fragile, multi-level house of cards: one wrong bump and it all falls down.

There is a better way!

So, I offer an easy-to-implement solution: where possible place your functions and properties in Class-like objects and all will be so much better. It’s not quite what you get with strongly-typed object oriented languages like C# or Java, but it works. Besides, if you use this pattern in your project then cats and dogs will live peacefully side-by-side and the universe will be in balance. The best news: this works perfectly fine with plain old JavaScript (POJO). And, if you are using something like jQuery use can use some version of Classes with those too, and I highly recommend it.

Here’s the fundamental pattern of a JavaScript Class:

function Add(){};
var someMath = new Add();

That’s very easy…right?? The advantages are many and include the following:

  • You get a powerful framework for logically grouping functionality. This lends to scalability and ultimately stability in your projects.
  • You can easily extend this pattern using prototypal inheritance.
  • You can take advantage of encapsulation.
  • You can implement inheritance and polymorphism.
Now let’s put this pattern to use. I’m providing two examples here. There are several other syntactical ways of doing this, but for brevity I’m sticking with two. The first example uses a very basic Object to implement namespace-like behavior. I say namespace-like because it’s not a true namespace like in C# or Java. The second example uses the built-in windows Object as a way of passing the namespace information. Click here to download a sample app that demonstrates these concepts.

POJO Class Pattern using Object Namespaces

Here’s an expanded example of the pattern with two levels of namespace separation using a standard Object :

if (!com) var com = {};    //1st level namespace
if (!com.ag) com.ag = {};  //2nd level namespace
if (!com.ag.Add) {
    com.ag.Add = function (value) {
        /// <summary>Demonstrates the plain old JavaScript pattern for classes.</summary>
        /// <param name="value" type="Number">Any number.</param>

        this.getValue = function () {
            /// <summary>Returns the property passed to the constructor.</summary>
            /// <returns type="Number">A number that was passed to the constructor.</returns>
            return value;
        },

        this.add = function (number) {
            /// <summary>This method adds value property + number</summary>
            /// <param name="number" type="Number">The number we want to add.</param>
            /// <returns type="Number">The number passed to the contructor plus this number</returns>
            return value + number;
        }

        //For Visual Studio intellisense cues
        com.__namespace = true;
        com.ag.__namespace = true;
        com.ag.Add.__class = true;
    }
}

And, here’s how to use this class:

var test = new com.ag.Add(2);
alert(test.add(4)); //displays "6"

POJO  Class Pattern using window[] Namespaces

Here is a slightly different version of the pattern that uses the window object, the results are the same:

if (!window["NS"]) window["NS"] = {};

window["NS"].Add = function (value) {
    /// <summary>This class uses addition</summary>
    /// <param name="value" type="Number">The number we pass to the contructor.</param>

    this.getValue = function () {
        /// <summary>Returns the private property called value.</summary>
        /// <returns type="Number">The number passed to the contructor.</returns>
        return value;
    },

            this.add = function (number) {
                /// <summary>This method adds value + number</summary>
                /// <param name="number" type="Number">The value we want to add.</param>
                /// <returns type="Number">The number passed to the contructor plus this number</returns>
                return value + number;
            }

    //For Visual Studio intellisense cues
    window["NS"].__namespace = true;
    window["NS"].Add.__class = true;
}

Here’s an example of how to use this class:

var test = new NS.Add(2);
alert(test.add(4)); //displays "6"

You are probably wondering about the funky xml comments. That’s for Visual Studio 2010’s built-in intellisense. I think but I’m not 100% certain that these work with Visual Studio Express as well. If you know for sure then please drop a comment. Notepad++ and other tools are okay for small projects but you can thank me later when you use this Class pattern along with built-in intellisense for any project that involves more than a dozen or so functions. And that’s not all – you can also see intellisense across different .js libraries. It’s all about productivity and ease-of-debugging. And, everyone will thank you when they have to re-use your code.

I’ve also attached a screenshot below to show you what the Visual Studio intellisense looks like. Also note in this example, the physical file, Add.js, in in the directory /scripts/com/ag/Add.js and I’m writing code in index.html which is at the root directory. How cool is that?!

 References

Sample application that demonstrates the Class concepts

Douglas Crockford’s [awesome] JavaScript Blog

Visual Studio JavaScript Intellisense Revisited

Creating Advanced [JavaScript] Web Applications with Object Oriented Techniques

Object Oriented Programming in JavaScript

Write Object Oriented JavaScript Part 1

The Format for JavaScript Doc Comments (Visual Studio)