Monkey Patching FlexSprite to list all event listeners on any Flex component
Posted by: Doug, in Flex, Flex 3, monkey patchingA while back Ben Clinkinbeard asked a question on flexcoders saying “Am I the only one who wishes EventDispatcher exposed its listeners?” The fundamental issue was that he wanted to get a list of all the event listeners that had been added to a given component.
The approach: using FlexSprite
Someone suggested monkey patching UIComponent to add this functionality, since each UIComponent gets notified each time a call to addEventListener is called, and you could keep track of the references locally. The thing is, monkey patching UIComponent is kind of a pain in the ass since it’s such a big class. When you monkey patch a class you basically need a complete copy of the whole class in your code so you don’t lose the functionality of that class. It’s perfectly doable with UIComponent, but gets kinda nasty.
So I was looking for an alternative and realized that the beautifully small FlexSprite class, which is the lowest level class you can get source code for (everything above it like Sprite, etc is built into the player). FlexSprite is under 100 lines of code and almost all of that is comments. When you get down to it there’s only a constructor and a single method in FlexSprite. So that makes it super duper easy to monkey patch the bastard. To refresh everyone’s memory the inheritance goes: Sprite -> FlexSprite -> UIComponent.
So if you’re trying to add in functionality that will trickle down into every component in the Flex framework, and you don’t need stuff specific to UIComponent, then the easiest way is to monkey patch FlexSprite.
The result
The good news: you can get a list of every event listener that is currently added to any component (at least anything that inherits from FlexSprite). This list will tell you the main details of the listeners, like type (ie “change”, “click”, etc), priority, useCapture, and useWeakReference. You also get the time when the listener was added (which is stored using getTimer to indicate the number of milliseconds since the app launched).
So now anything that inherits from FlexSprite (every display object in the Flex framework) has the following additional property and methods:
- eventListeners - returns an Array of every event listener currently registered with the component. The Array contains ListenerTracker objects, which is a new class that simply holds the details of the listeners (like priority, etc).
- removeEventListeners(type:String) - this will remove all event listeners for a given event type. You don’t have to have the reference to the actual listener function to remove it.
- removeAllEventListeners() - removes all event listeners of all types. This completely wipes out all event listeners for the component all at once.
Here’s a very simple demo application that simply adds a button via MXML and shows all the event listeners on that button in a DataGrid. You can click the bottom button to remove all the listeners on the top button, which will effectively disable all interaction with the button, which should make sense.
The bad news: so you get the function that was added as the listener, but you can’t get anything useful out of this, like the actual function name in the code. You also can’t get the name or reference of the object that contains the function, or that made the call to addEventListener. So that blows.
UPDATE: We can actually get the name of the class that called addEventListener(), the method name where it was called, and the exact line number. See my post about a sneaky trick to do this.
Basically you can see how many listeners of each type (and priority, etc) there are and you can remove any individual ones (or all of them at once). But we are not being able to get much information about a Function object itself. In the addEventListener function we get passed a Function, but you can’t get any more information about that object (see the listener column in the above example, it just shows the “function Function () {}” string, which doesn’t help at all). I’ve tried using describeType() on the Function and everything else I could think of but couldn’t figure out how to get any additional useful information about a function. If anyone has any ideas shoot me an email to let me know.
A cooler example
So I remembered seeing a pretty badass Flex library that let you explore all the properties and styles of any ui components in your application. I went back and found it, it’s called flexspy and is a project hosted on Google code created by Arnaud Pichery. So that’s cool, you drop the FlexSpy swc into your app and suddenly you get access to this cool real-time property and style inspector. (BTW, FlexSpy is licensed under the Do What the Fuck You Want To Public License, ha, awesome)
So I went and extended the FlexSpy component and added my own Event Listeners tab to the main inspectors. Now when you launch my version of FlexSpy you can see all the event listeners on whatever component you select. Dope. I added in buttons to let you remove any individual listeners, or all the listeners for any particular component. Here’s a screenshot, and the links to the example app and the source (view source is also enabled on the app).
Enhanced FlexSpy demo | source
















Entries (RSS)
February 21st, 2008 at 11:09 am
Dude, that rocks.
February 21st, 2008 at 11:32 am
[…] I had been thinking of doing something similar since I had seen requests on Flexcoders and thought this would be a useful and doable thing, but Doug beat me to the punch: Monkey Patching FlexSprite to list all event listeners on any Flex component Also, a very nice use of FlexSpry […]
February 21st, 2008 at 11:37 am
Badass app Doug…as usual!
February 21st, 2008 at 1:45 pm
Hi Doug,
That’s something I really wish sometimes that event dispatchers would have. Although monkey patching is evil, I’m certainly going to use this in some debugging situations.
I’m not sure that really what you wanted to obtain, but you can get the function that called the addEventListener like this ():
package {
import flash.display.Sprite;
public class FunctionName extends Sprite
{
public function FunctionName()
{
showMyName()
}
private function showMyName():void
{
var e:Error = new Error();
var s:String = e.getStackTrace();
var i:int = s.indexOf(”]”) + 6;
var j:int = s.indexOf(”()”, i);
trace(s.substring(i, j));
}
}
}
(Inspired by Alex Harui’s debugging tricks post)
February 21st, 2008 at 3:08 pm
[…] Contact « Monkey Patching FlexSprite to list all event listeners on any Flex component 21 02 2008 […]
February 22nd, 2008 at 12:08 pm
Doug, I’ve been wondering how monkey-patching will play with the Flex framework caching… As in, if you opt to use the framework caching, can you still use monkey-patching? Thoughts?
February 22nd, 2008 at 6:45 pm
[…] I was just reading Doug McCune’s blog where he discusses FlexSpy, which is covered under the Do What The Fuck you Want Public License. […]
February 26th, 2008 at 12:53 pm
[…] FlexSpy with Event Listeners I decided to show this example even though I had already released the code on my blog. I realized that this example was perfect for showing the benefit of keeping a running list of open source projects in your head. I remember seeing FlexSpy when it first came out, I read about it on a few blogs and briefly checked it out by playing with the example. But I didn’t ever download the code or use it in my app. And then last week when I was monkey patching FlexSprite to add extra event listener functionality and I realized that this was a perfect place to use FlexSpy. I couldn’t remember the exact name (I thought it was something like Flexray), but I googled around, determined to find the project that I knew existed. Pretty quickly I found it, pulled it down, started reading the source and hacked in the functionality I wanted. […]
February 28th, 2008 at 1:26 am
[…] Hey Doug…you feel like adding another tab to your custom FlexSpy? […]
April 29th, 2008 at 11:28 pm
Doug, very nice!
I set the button as visible=”false” and tried to access the panel via Ctrl+F12 and it seems that only the flexspy portion comes up. Anyway to also have your portion also show up using Ctrl+F12. This option is nice as to not have to have the button show all the time.