I'm having difficulty with the last piece in the puzzle on AS3 events.
I understand target classes inherit from EventDispatch or implement IEventDispatch and can register (amongst other methods) event listeners.
However what do the target classes register with? If an Event happens, how does AS3 know to pass the Event to the target classes?
Regards,
shwell.
Read this article about event phases and it will make more sense:
http://livedocs.adobe.com/flex/3/html/help.html?content=events_02.html
Hope this helps. Have a great day.
You can look at how starling event works
starling even dispatcher
When a displayObject bubbles an event, it will check if the parent of the displayObject exist and add the parent to bubbleList if exist util the ancestor of displayObject is null.
The following code is in starling eventDispatcher
var element:DisplayObject = this as DisplayObject;
var chain:Vector.<EventDispatcher> = new <EventDispatcher>[element];
while ((element = element.parent) != null)
chain[int(length++)] = element;
In AS3, EventDispatcher is an implementation of the observer design pattern. This class implement the addEventLister, removeEventListener, dispatchEvent' andhasEventListener` methods. Internally, it also maintains a dictionary or similar data structure that contains the events which are currently being listened for, and a list of methods which have to be called when the event is dispatched. Something like this -
{"event1": [method7, method5, method3], "event2": [method3, method2], "event3": [method1]};
When addEventListener is called on an object, it creates a new key for the event in question and adds the method reference to its associated value list.
When dispatchEvent is called on the class, it fetches all the methods associated with the event and calls the methods attached with it. Each method is called with an instance of the Event class or its subclasses.
Removing an event listener obviously does the opposite of what adding does.
I guess you're missing of addEventListener() mechanics. This thing has a global side effect on event engine, registering a callback function along with caller this value to provide correct context of a fired event, with possible update of event.localX and event.localY properties by calling globalToLocal() either statically or dynamically, as the event bubbles up and down.
If you are, like me, confused about how does Flash player determine the target of an event - there is an internal "focus" pointer that determines which component of the SWF has keyboard focus, and that one is used to target keyboard events. For mouse events, most likely Flash engine calls getObjectsUnderPoint() to query for topmost IEventDispatcher compatible objects (not all of the DisplayObjects can process events), and that one is sent a mouse event, with the previous event's target to receive a say MouseEvent.ROLL_OUT or MouseEvent.MOUSE_OUT if the target has been changed. For other events, most likely the entire display list can react.
For objects in the display list, the following excerpt from Adobe is the answer "When Adobe® Flash® Player dispatches an Event object, that Event object makes a roundtrip journey from the root of the display list to the target node, checking each node for registered listeners.".
For non display objects, AS3 run time maintains a dictionary of all AS3 events containing bound variables. The bound variables are a reference to the event listeners.
Related
Usually when dispatching an Event (or custom Event) I use this:
function fireEvent():void {
dispatchEvent(new Event(Event.COMPLETE));
}
Is better use a single object to dispatch an event every time? Can I do this?
var myEvent:Event = new Event(Event.COMPLETE);
function fireEvent():void {
dispatchEvent(myEvent);
}
It would be a way better (eg create event pools) if it works. Unfortunately AS3 does some internal magic with event object during event flow which makes objects only one time usable (Sorry, by some reason I can't find proof quickly).
You can easily get confirmation by dispatching event second time, your listeners will not be triggered (may be it's true only for bubbling feature, not remember exactly), so you have to create a new instance of event object for every dispatchEvent call.
It doesn't worth it even if pooling works, I presume. Creating event objects isn't expensive in astionscript, adding event listeners is. You'd better put this effort on managing listeners and handlers which could easily become the performance buttleneck.
Now I am working on Flex 4 with AS3. In most of time I am using the addEventListener.
This listener default arguments are type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false. But most of the developers doesn't consider the last 3 arguments. But I want to know the uses of these 3 arguments?
Anyone call tell me answer?
Most of the times those three last parameters aren't necessary, because it's usually better to use a programming pattern to manage dependencies rather than leaving it to hard-to-debug spaghetti code that goes through many other classes.
Yet, there is a purpose for them and they can come in very handy:
useCapture - To register a listener with an event target’s ancestor for the capture phase of an event
dispatch, we set addEventListener( )’s third parameter, useCapture, to true, as in:
theAncestor.addEventListener(myEvent, MyListener, true);
The code causes MyListener( ) to be executed whenever AS3 dispatches myEvent targeted at one of theAncestor’s descendants before that descendant receives notification of the event.
priority - if you add more than one event listener to an object, the one that has higher priority will be triggered first. If you add two or more listeners with same priority, the one that was added first will trigger first. Just imagine that upon creating an object you've added an event listener, but later on you need to add another one and you want that new one to run first. Registering it with higher priority should do the trick.
weakReference - this is not a commonly used one, because Flash's garbage collector can run any time or even never. The problem is that regardless of any use of weak references, an object may continue to dispatch and listen to events until it gets garbage collected.
By default, an object that registers a listener
for a given event maintains a reference to that listener until it is explicitly unregistered
for that event—even when no other references to the listener remain in the program.
By setting the value to true the object will lose the reference to the listener when the object is eligible for garbage collection.
It's used to prevent memory leaks, but useWeakReference only has an effect when the listener object has a shorter lifetime than the dispatcher. It's a good idea to understand how to use it, but in practice I avoid it and simply write a method in my class that would remove all the added listeners.
My application needs to notify UI that sound playback is finished. To accomplish that is attaches listener to the SOUND_COMPLETE event of a SoundChannel object.
Should I remove my SOUND_COMPLETE event listener after event processing is done?
private function playbackCompleteHandler(event:Event):void {
// Notify UI that playback is done etc
channel.removeEventListener(Event.SOUND_COMPLETE, playbackCompleteHandler);
}
Everyone says that we should always remove event listeners so that GC could properly collect objects ('channel' object in this case). But it seems that Adobe doesn't do that in the official documentation [1][2]
[1] http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/SoundChannel.html
[2] http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7d21.html
Dispatcher holds listeners, so, if your dispatcher is long-living(like Application), you should use weak listeners:
channel.removeEventListener(Event.SOUND_COMPLETE, playbackCompleteHandler,false,0, true)
last true allows your objects to be freed.
If you don't need your temporary objects to process dispatcher's events after you loose all references to temporary objects and until they are collected, you shall remove listeners
Otherwise(when dispatchers are short-living), there is no need to care about listeners: if you are listening to something temporary, it will die without any problems.
It seems that your case is the last one, so it's better not to pollute code with meaningless lines.
You're question states my answer for this
Everyone says that we should always remove event listeners so that GC
could properly collect objects
If you are no longer keeping an instance of the Sound object around; then remove the event listener. If you will still be using the Sound object; then there is no need to remove the listener.
I have a custom component called shopView which is an MXML skinnable component. I have a controller class called ShopController which takes care of showing this component in popup, updating info shown in the component etc.
Now, I wanted to maniupate some of the subcomponents of this ShopView after it has been created from the controller after the ShopView is created (creationComplete() event)
So, I have attached an event listener which intern does some initialization process
creationComplete="init(event)"
the init() function
private function init(event:FlexEvent):void{
event.stopImmediatePropagation();
initMenus();
initSlots();
dispatchEvent(event);
}
Attached another creation complete event from the controller class
_shop.addEventListener(FlexEvent.CREATION_COMPLETE,onShopCreated);
*_shop is the instance of ShopView*
Now, if you see the init() function, there I am stopping the event propagation, doing some initialization process and after that I am dispatching the event (for the shop controller do the rest of the job)
Now, this is crashing the app because the crationComplete event of the ShopView is recursively called. I was thinking the dispatchEvent will propagate to the other listerners but seems like it is propagating back to the same component.
I have fixed it by removing the e.stopImmediatePropagation() and dispatchEvent(event) lines from the init() function. But I want to know why it is happening like this?
Is it a known issue for the mxml/flex components? OR it is expected behavior?
Update: I am not doing same in .as as I said below. Got answer, basically its my stupidity :)
because I have not seen this behavior when I write .as classes where I
stopevent propagation and dispatch the event based on business logic.
Thanks in advance.
This is expected behavior.
When you redispatch an existing event dispatchEvent automatically clones it (since you can't dispatch the same event twice.) This clears any propagation-related flags.
May I ask why you want to redispatch CREATION_COMPLETE in this situation anyway? Both handlers will function just fine without the two lines you removed.
I am working on an AIR application:
The main window is like a dashboard. With the menu bar, I can open other windows with dashboard details. When I close these, I'd like to refresh the main window.
I tried to use an event listener, but the result is not good. If I open detail windows directly from the main window, I know how to add an event listener - and it works - but I don't know how to do it, if the detail window is opening from the menubar!
Thanks for helping me.
A Singleton is what you are looking for. Just put an event dispatcher inside and you will be able to listen from everywhere in the application.
A Singleton is like having a unique instance of an object in memory, so anyone modifying a variable inside that object ( or sending events throught ) will be modified for everyone.
Here is an example of code on how to use it.
http://life.neophi.com/danielr/2006/10/singleton_pattern_in_as3.html
Note: Singletons are powerful and dangerous at the same time, there is a lot of talk about how to use them, please read a little more about that if you are considering building a big project.
Hope it helps!
The issue is that you're performing business logic from a View. Don't do this. Instead, dispatch an event from each menu rather than directly opening the window from within it. Listen for those events at a higher level, and then you can either directly listen to the new windows you have opened, or you can create a base window Class that exposes a variable of type IEventDispatcher. If you populate that variable with the same event dispatcher, what you wind up with is called an "event bus," and you can listen on that for events.
This architecture requires a little more thought than using a Singleton, but it avoids the tight coupling and other issues you'll run into by introducing one into your project.
You can listen to an object (EventDispatcher) directly by adding an event listener to it, or if the dispatcher object is on the displaylist, such as a Sprite, you could listen at the stage level with the capture parameter set to true.
But the main caveat is that the dispatcher must be on stage for you to catch this event.
Your main window listens to stage (with capture = true):
stage.addEventListener("MY_CUSTOM_EVENT", handle_custom_event, true);
private function handle_custom_event(e:Event):void
{
var sub_window:Object = e.target;
// do something to your sub_window
}
Your sub window can dispatch events like this:
dispatchEvent(new Event("MY_CUSTOM_EVENT"));
But (ab)using the stage as a message passing infrastructure for custom events in this way is a little messy. You could consider a more formal message passing architecture if you really want this kind of communication. Even a static MessageBus class would at least quickly help you identify where you use this in your codebase. Either way, you'll have to be careful about references and memory leaks.