Eclipse Search

Loading

Dec 17, 2008

ListenerList - a better way to manage your event listeners

You have published a listener interface and have a place where your clients register their listeners. There you typically code like this:

List<SomeEventListener> listeners = new ArrayList<SomeEventListener>();

public void addSomeEventListener(SomeEventListener listener) {
    listeners.add(listener);
}

public void removeSomeEventListener(SomeEventListener listener) {
    listeners.remove(listener);
}

public void fireSomeEvent(SomeEvent event) {
    for (SomeEventListener listener : listeners) {
        listener.eventOccured(event);
    }
}

The problem with this approach is two fold:

  1. Memory usage: The capacity of the ArrayList is always >= its size. So if you just have 200 listeners, there is a good chance that the memory might have been allocated for 300 elements
  2. Thread safety: Murphy's law will prove itself, and multi threaded bugs can be a pain in your back to debug. You have to properly sync all the write access and provide faster read only access for event firing operations.

To handle these two issues, Eclipse gives you a nice API: ListenerList class. Its meant for storing these kind of listeners, where any write modification to the underlying array (add, remove, clear) is synchronized and give access to the underlying array for faster read access. Memory is allocated only for the number of listeners registered.

The same above example can be rewritten as:

ListenerList listeners = new ListenerList();

public void addSomeEventListener(SomeEventListener listener) {
    listeners.add(listener);
}

public void removeSomeEventListener(SomeEventListener listener) {
    listeners.remove(listener);
}

public void fireSomeEvent(SomeEvent event) {
    Object[] listenersArray = listeners.getListeners();
    for (int i = 0; i < listenersArray.length; i++) {
        ((SomeEventListener)listenersArray[i]).eventOccured(event);
    }
}

ListenerList provides two ways of duplicate checking of the listeners: Equality and Identity. It can be specified in the constructor. Since the core Eclipse platform is in Java 1.4, you still can't use generics and foreach, but that shouldn't be a hindrance.

Question: Now that I do have a direct access to the underlying array, what if i manipulate it like this:

Object[] listenersArray = listeners.getListeners();
listenersArray[0] = null;
listenersArray[1] = someEventListener5;

Well, the same thing happens when you shoot yourself in the foot :-)

4 comments:

  1. If you want to use the right data structure from the standard library, it should be CopyOnWriteArrayList. It was only added in Java 5 though.
    ReplyDelete
  2. I just use the 1.5 CopyOnWriteArrayList. Works well with my own project, contains everything I need for thread safety, and also with some of the Eclipse projects like CDT and Mylyn, and means I have one less specialized component to learn.

    For most components, if I have a concern about a listener's size versus capacity, I have other scalability issues to consider. :) Building something like SWT may be another matter.
    ReplyDelete
  3. CopyOnWriteArrayList or Arrays.copyOf - all are standard APIs, but require newer JDKs. But considering the mobile and embedded space, the Eclipse core platform is struck with JDK 1.4
    ReplyDelete
  4. Did you know that in JDK there is EventListenerList. It do the same thing.
    ReplyDelete