Home >
Managing event listeners can get tricky as your ActionScript application grows. This technique takes advantage of overriding the core addEventListener and removeEventListener methods inherited in any classes that extends the EventDispatcher. When adding listeners, we will store a reference of it as a property of the registeredListeners Object using "type" as the key and its "listener" for the value. When removing the listener we will delete it from the registeredListeners Object. Finally to clear all the listeners, we loop through the registeredListener's properties and remove any listeners that were registered. Here is the basic set up:
The variable you will need:
protected var registeredListeners:Object = new Object();
Functions you will need:
/**
* This overrides addEventListener. When called it passes the supplied
* params back up to the super of the function and registers the type
* and listener with the registeredListeners Object.
*/
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
registeredListeners[type] = listener;
}
/**
* This overrides removeEventListener. When called, it passes the supplied
* params back up to the super of the function and deletes the type
* and listener from the registeredListeners Object.
*/
override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
{
super.removeEventListener(type, listener, useCapture);
if(registeredListeners[type])
delete registeredListeners[type];
}
/**
* This clears all of the listeners by looping through the
* registeredListeners object, validates that they are active,
* removes them, then creates a new registeredListeners Object.
*/
public function clearEventListeners():void
{
var type:String;
for(type in registeredListeners)
{
if(hasEventListener(type)){
removeEventListener(type, registeredListeners[type]);
}
}
registeredListeners = new Object();
}
This is all the code you need to organize your listeners. In addition to these functions, I usually create a destroy method on all of my classes that automatically calls clearEventListeners and helps me perform other cleanup actions. Having this functionality can greatly help in optimizing your code and lowering your application's memory footprint by correctly removing listeners for the Garbage Collector to cleanup. As an added bonus you can always check the total amount of listeners on a class, their types, and their listener functions by adding logic to query the registeredListener Object's properties. Finally in this example I used an Object to store the key value pairs of each listener but in a real work app I would probably use a Dictionary or some form of a HashMap for an added speed boost.




Facebook Application Development
Nice post! Will help me clean up event listener messes that pop up in the future.
Won't your remove method nix all of my listeners even if I just want one done?
Example:
addEventListener(Event.COMPLETE, item1.handleComplete);
addEventListener(Event.COMPLETE, item2.handleComplete);
addEventListener(Event.COMPLETE, item3.handleComplete);
addEventListener(Event.COMPLETE, item4.handleComplete);
addEventListener(Event.COMPLETE, item5.handleComplete);
Then calling:
removeEventListener(Event.COMPLETE, item1.handleComplete);
basically nixes every one of those above. Not good.
You also couldn't add that many listeners of the same type/name.
Your approach works but implementation isn't quite fluid yet.
Thoughts?
Jesse-
If you set the useWeakReference to "true" then that should fix your garbage collection problems. Beyond that, it seems like the object adding a listener should be the one to manage its listener and not the object that's being listened to.
Ben
There is a big problem with this implementation, as John C Bland points out. But not only when it's time to remove listeners, but also when adding them:
myDispatcher.addEventListener(Event.COMPLETE, handleComplete1);
myDispatcher.addEventListener(Event.COMPLETE, handleComplete2);
In the registeredListeners object in your code, only the latter of these will be registered, as the reference to the first one was overwritten when the second one was added. This means that the clearEventListeners() will not know of all the listeners, and you couldn't rely on it to remove all listeners.
It's a pretty good idea though. It's just the implementation of it that's a bit lacking.
Cheers
/R
John, you bring up an interesting point. The above example is not without its flaws, it was intended to inspire people to play with the notion of event management. Also, this should be part of much larger set of logic you would customize to fit your specific needs.
To answer your question, I usually don't to attach the same listener types to multiple handler methods in an attempt to avoid conflicts. After seeing your code I would do something like this with a single listener:
addEventListener(Event.COMPLETE, onComplete,...);
Then capture all Event.COMPLETE broadcasts in a single handler with a switch statement to rout them to the appropriate place. This is a common issue when having a sprite with multiple buttons or a preloader class with multiple Loaders. You wouldn't want to have listeners on each instance so putting a single listener on a parent class, taking advantage of event bubbling, then validating the target of the event to perform a specific action would lower your overall listeners and avoid conflicts in methods being called at the same time. Granted I can also see problems with this as well.
Unfortunately there is never a single answer on how to correctly implement code and it depends greatly on your coding style. Thanks for the feedback and I would love to see how people customize this for their own applications. If this is a popular issue I would be more then happy to create a more detailed tutorial on architecting this kind of solution.
Sincerely,
Jesse
You can assign an array of listeners (functions) to registeredUsers[type] and just push every listener bound to this event type. Combined with monkey patched flash.events.EventListener you can have quite improved usage of event listeners.
Hey Dimitar, I just did a quick google search on "monkey patched flash.events.EventListener" and came across this http://dougmccune.com/blog/2008/02/21/monkey-patching-flexsprite-to-list-all-event-listeners-on-any-flex-component/. It looks like a interesting solution if you are using Flex. I am not a fan of Monkey Patching but I have used it from time to time back in AS 2 when I refused to use prototype.
I was just digging through the source code in the example and although it is similar to the teqnuqie I posted about it does offer up a more robust solution to John & Richard's concerns about managing multiple handlers to similar event types.
This is great, thanks for the comment.
- Jesse
You may also want to check out the Swiz framework (http://code.google.com/p/swizframework/ by Chris Scott). It has a central event dispatcher using a singleton to manage the Event listeners (adding and removing), allow listening and dispatching, to and from anywhere in your application. They're regularly updating the framework, so new features are added all the time.
@Jesse
I hear you on event bubbling vs listeners on each item. I use bubbling sparingly since Event.COMPLETE could kick off some other stuff up the line since "complete" is a common event. I know...I could cancel it but if I want it to bubble, based on individual item changes, I don't want it stopped by the parent. So...yeah, different strokes for different folks.
@Gareth
My issue with frameworks is that exact issue. Your entire app is bound to Swiz and changing it to Mate or Cairngorm (which is just as strict). Anyways...that's another convo but IMO using Swiz may help with event listener management but you bring up other big issues.
Great Post!
Here is a solution to removing only the correct event listeners.
/**
* This overrides addEventListener. When called it passes the supplied
* params back up to the super of the function and registers the type
* and listener with the registeredListeners Object.
*/
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
var eventListenerVO:EventListenerVO = new EventListenerVO(type,listener);
registeredListeners.push(eventListenerVO);
}
/**
* This overrides removeEventListener. When called, it passes the supplied
* params back up to the super of the function and deletes the type
* and listener from the registeredListeners Object.
*/
override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
{
super.removeEventListener(type, listener, useCapture);
var eventListenerVO:EventListenerVO;
for(var i:int=0; i {
eventListenerVO = registeredListeners[i];
if (eventListenerVO.type == type && eventListenerVO.listener == listener)
{
registeredListeners.splice(i,1);
}
}
}
/**
* This clears all of the listeners by looping through the
* registeredListeners object, validates that they are active,
* removes them, then creates a new registeredListeners Object.
*/
public function clearEventListeners():void
{
var eventListenerVO:EventListenerVO;
for(var i:int=0; i {
eventListenerVO = registeredListeners[i];
removeEventListener(eventListenerVO.type,eventListenerVO.listener);
}
}
internal class EventListenerVO
{
public var type:String;
public var listener:Function;
public function EventListenerVO(type:String,listener:Function)
{
this.type = type;
this.listener = listener;
}
}