Loader.loadBytes and getDefinitionByName - apache-flex

I am currently working on a project that will load a swc, inspect it and allow the user to view the classes inside.
I load the library.swf using Loader.loadBytes (the bytes come from the unzip library I use). I create an instance of the class using getDefinitionByName.
This all works fine as long as getDefinitionByName is called on the next frame. If I call it straight away I get a reference error. To get round this I've come up with a rather hacky solution:
private function processLibraries( event : Event ) : void
{
_zipFiles.forEach( processSwfs );
DisplayObject( FlexGlobals.topLevelApplication ).addEventListener( Event.ENTER_FRAME, enterFrame );
}
private function enterFrame( event : Event ) : void
{
DisplayObject( FlexGlobals.topLevelApplication ).removeEventListener( Event.ENTER_FRAME, enterFrame );
_classCollection = new ArrayCollection();
_zipFiles.forEach( processCatalogs );
complete( _classCollection );
}
I really don't like using the enter frame event on the top level application. I also don't want to have to set up a timer. That's just as nasty.
Loader.loadBytes doesn't fire a complete event so I don't know where I listen for an event for when the bytes have been fully loaded into the ApplicationDomain.
There must be a neater way round this?
Thanks

Loader.loadBytes does fire a complete event. Remember to add your listener to Loader.contentLoaderInfo.
However, if you are loading a SWF file which was part of a SWC file compile with a FlexSDK prior to version 4 you will not get the Event.INIT method since it is only fired when a document class is availalable. Such a class is injected by the compiler since SDK version 4.

Related

Is there anything similar to AWTListener in JavaFX?

I am looking for a way by which I can capture all events (specifically keyboard/mouse events) in a JavaFX application. Is there anything similar to AWTListener in JavaFX? In Java, by creating a AWTListener and adding it to default toolkit I can capture all the events.
Adding some background
I am involved with Marathon, a test automation tool for Java/Swing applications, and currently in the process of evaluating how complicated it is to add JavaFX support. By design, I do not have access to the JavaFX application itself. For Java/Swing we use javaagent to load Marathon hook into the AUT and use AWTListener to listen for all events. I suspect that something similar should be possible for JavaFX.
Not sure how exactly your goal can be achieved, but I would start by taking a look at the EventDispatcher of a Scene.
final EventDispatcher eventDispatcher = scene.getEventDispatcher();
scene.setEventDispatcher( new EventDispatcher()
{
#Override
public Event dispatchEvent( final Event event, final EventDispatchChain tail )
{
System.out.println( "Dispatching event [" + event + "]." );
return eventDispatcher.dispatchEvent( event, tail );
}
} );

why need to remove creation complete listener in its handler?

I found the following code snippet from this open source project Loading Spinner
public function Spinner() {
super();
addEventListener(FlexEvent.CREATION_COMPLETE, handleCreationComplete);
}
private function handleCreationComplete(e:FlexEvent):void {
removeEventListener(FlexEvent.CREATION_COMPLETE, handleCreationComplete);
if (autoPlay) {
play();
}
}
why the line of removeEventListener is needed? Do that mean a creationComplete event will be fired more than 1 time?
The only reason for removing the listener is this:
As long as an event listener is attached to an instance of a class, that listener can never be "garbage collected" until it is removed from the dispatcher, or until the dispatcher is also eligible for garbage collection. i.e. it will keep existing in memory as long as the application runs, even if you remove it from the stage and you explicitly set it to null.
That's why it's good practice to always clean up event listeners when they are no longer needed. You may avoid memory leaks.

Robotlegs Flex - How deal with mediators being initialized after creationComplete?

I'm trying to build a Flex application using the Robotlegs framework and don't know how to deal with the creation of mediators (-> onRegister being called) only after the creationComplete event of the view component.
My application loads several XML files at startup, models are created from the files and then dispatch events with the data.
Problem: When "loading" embedded/local files at startup, the model events are dispatched way before the mediators listeneres are added, although they're mapped before triggering the initial data load in the main context.
Is anybody using robotlegs with flex and has foud a "cleaner" way around this, than manually dispatching an event in the mediators onRegister? As doing so the "automatic" mediation would not really be automatic anymore ...
Edit:
Minimal example code:
Context:
override public function startup( ):void{
mediatorMap.mapView( EditorMenu, EditorMenuMediator );
commandMap.mapEvent( ContextEvent.STARTUP, LoadConfigurationCommand );
dispatchEvent( new ContextEvent( ContextEvent.STARTUP ) );
}
LoadConfigurationCommand:
[Inject] public var configurationService:IXMLLoader;
override public function execute():void{
configurationService.loadXML();
}
ConfigurationService:
public function loadXML(){
trace( "xml loaded" );
dispatch( new XMLLoadedEvent( XMLLoadedEvent.CONFIGURATION_LOADED, result ) );
}
EditorMenuMediator:
override public function onRegister( ):void{
trace( "menu onregister" );
addContextListener( XMLLoadedEvent.CONFIGURATION_LOADED, handleXmlLoaded, XMLLoadedEvent);
}
The trace "menu onregister" is happening way before the trace "xml loaded", so the mediator's not listening when the XmlLoadedEvent is dispatched.
I approach this with the StateMachine and grab control of the order of operations. Another approach here would be to add the listener, but also inject the model and check it for data in onRegister. Either approach should put you in front of the race conditions.
I'm not expert in RobotLegs (I'm actually a Parsley addict), however, if you're using a Mediator pattern, that means that your mediator has direct access to the view itself. How about you create an interface class for all your views that has an init public function which your mediator could call after the onRegister.
Maybe Robotlegs has another way of doing it with metadata or events or something, but this is still valid.

I can't dispatch custom event from one module to another as it gives TypeError: Error #1034: Type Coercion failed:

I am trying to dispatch a custom event from one flex module to another.
The code which dispatch the event is as below
Application.application.Destination.child.dispatchEvent(
new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
here AlgoEvent is a custom event
on the other side the module which catches and handles the event has this code:
public function sendParametersToChild(e:AlgoEvent):void
{
//some codes
}
but when the statement Application.application.Destination.child.dispatchEvent(new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT)); is executed the debugger give the following run time exception:
TypeError: Error #1034: Type Coercion failed: cannot convert resources.events::AlgoEvent#4182239 to resources.events.AlgoEvent.
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
at components::Destination/sendTimeToChild()[E:\FlexProjects\MyApp\src\components\Destination.mxml:99]
at components::Destination/updateParameters()[E:\FlexProjects\MyApp\src\components\Destination.mxml:206]
at components::Destination/__CreateBasketButton_click()[E:\FlexProjects\MyApp\src\components\Destination.mxml:558]
I am not able to identify what is going wrong here.
Please help to solve this problem
This is my Event class
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
private var eventType:String;
public function AlgoEvent(eventType:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(eventType,bubbles,cancelable);
this.eventType=eventType;
}
}
While debugging am getting error in this funcion of UIComponent class
override public function dispatchEvent(event:Event):Boolean
{
if (dispatchEventHook != null)
dispatchEventHook(event, this);
return super.dispatchEvent(event);
}
Excaxtly this line gives the error: dispatchEventHook(event, this);
Import the AlgoEvent class in the main application and create a reference to it.
import resources.events.AlgoEvent;
private var dummyEvent: AlgoEvent;
Some explanations for this could be found here: Module domains
If your custom event doesn't carry any special event properties you could workaround your problem by using the standard Event class.
dispatchEvent(new Event(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
I had the same problem when dispatching, solved overriding two functions:
override public function clone():Event
{
return new AlgoEvent(type, bubbles, cancelable);
}
override public function toString():String
{
return formatToString("AlgoEvent","type"","bubbles","cancelable","eventPhase");
}
hope it helps out :)
Mr. splash suggested a solution which worked fro me:
Try to make the Custum Event (Algo Event in my case) class known to the main application.
I.e import it in the main application and create a variable of it..
And it works for a main reason>>when we try to communicate betwwen the modules using event dispatching what happens is :the modules are loaded at the run time but the classes like event classes are linked to the modules at the run time..
But the Event class is compiled before the modules are loaded..
application defines a Custum Event Class at compile time, and the module defines its own Custum Event Class when it is published. Then when the application is run, the Custum Event Class dispatched in the application doesn't match the one in the module
swf.
For the problem which is causing this error one can check the link:
http://www.kirupa.com/forum/showthread.php?t=320390
and also
http://www.jeffdepascale.com/index.php/flash/custom-events-in-loaded-swf-files/
Mate framework takes care of all this.
It gives you a global event bus, for all modules in your app.
http://mate.asfusion.com/
Try to override the clone() method in your customized Event,AlgoEvent.
Add the following code to your AlgoEvent.as class and try:
override public function clone():Event{
return new AlgoEvent(eventType,bubbles,cancelable);
}
HTH.
Your custom Event class should look like this:
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
public function AlgoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
};
override public function clone():AlgoEvent
{
return new AlgoEvent(type, bubbles, cancelable);
};
};
You should use the Event's inherited type property instead of creating a new one.
Also, the UIComponent has it's own dispatchEvent method, so you don't have to create your own - only if it works differently to the inherited one.
Regards,
Rob
Okay, it must be said that what you're doing, from an architectural standpoint, is wrong. Calling Application.application is bad for so many reason, especially if you're then starting to go down the display tree. The second any of the children changes, your build is now broke, and you won't know that until runtime because it's a module.
What you need is an application framework. A way to increase complexity without decreasing maintainability. There are many out there, but my personal favorite is Parsley. I've used it on many very large projects with much success. The problem you're trying to solve right now, dispatching one event where the other module listens for it, is extremely trivial (can be done in about 3 lines of code).
I recommend you look it over as well as my presentation on an introduction to parsley.

FLEX: dialog not display immediately

In an AIR application I have the following code:
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
theDialog.addEventListener(FlexEvent.CREATION_COMPLETE, cpuIntensiveCalc);
At the end of cpuIntensiveCalc the dialog is removed. The dialog informs the user that "something is going on, please stand by."
The problem is that cpuIntensiveCalc starts before the dialog draws. So the user experience is that the application freezes for 10 seconds with no indicator, then the modal dialog flashes quickly (less than a second) on screen.
The Adobe docs say this about creation_complete
Dispatched when the component has finished its construction,
property processing, measuring, layout, and drawing.
So this feels like the correct event.
In the name of completeness, I also tried
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
cpuIntensiveCalc();
But had the same results.
TIA
The reason for this is that the Flash Player is single threaded, and so you are blocking the UI from reacting to the Dialog Popup until the maths chunk is finished.
Hacky fix time...
You have two options.
(This one should work, but is untested) Wrap the cpuIntensiveCalc() call in a callLater, so that the UI can finish rendering before you block the rendering.
Or
Use "Green Threads" to break up your processing so that you don't completely block the UI processing. Take a look.
(I just had the same issue => even if this thread is old, I just wanted to contribute my solution)
(disclaimer: this is a bit ugly, but they say that's ok in the UI layer... ;-) )
Flex is single threaded (at least from our developer's perspective, I think behind the scene threads are used by the VM)
=> you typically execute your code in the UI thread, after the user did some action on a widget. Any call to update a UI component (like setProgress or setLabel) will only be rendered on screen at the end of the render cycle (see again UiComponent life cycle).
=> In therory calling "cpuIntensiveCalc" in a callLater will let the framework display your popup before executing the method.
In practice though, I noticed you typically have to have for a couple of UI cylces before the popup be displayed, like this:
new MuchLaterRunner(popup, 7, cpuIntensiveCalc).runLater();
MuchLaterRunner being defined like this:
public class MuchLaterRunner
{
var uiComp:UIComponent;
var currentCounter = 0;
var cyclesToWaitBeforeExecution=0;
var command:Function;
public function MuchLaterRunner(uiComp:UIComponent, cyclesToWaitBeforeExecution:uint, command:Function)
{
this.uiComp = uiComp;
this.command = command;
this.cyclesToWaitBeforeExecution =cyclesToWaitBeforeExecution;
}
public function runLater() {
currentCounter ++;
if (currentCounter >= cyclesToWaitBeforeExecution) {
uiComp.callLater(command);
} else {
// wait one more cycle...
uiComp.callLater(runLater);
}
}
}
The issue is the same when calling setProgress afterward: we must divide cpuIntensiveCalc into small callable methods that can be ran at each UI cycle, otherwise the progressbar won't, err, progress.
Use enterFrame event on popup. Don't forget to remove the listener in the enterFrame event handler - otherwise the cpu intensive method will be called in each frame, crashing your app. If this doesn't work at first, use a private number as a counter and keep incrementing it in the enter frame handler - call cpu heavy method only when the counter reaches the appropriate value. Find the 'appropriate' value by trail and error.
theDialog = PopUpManager.createPopUp(this, TheDialogClass, true) as TheDialogClass;
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private function onEnterFrame(e:Event):void
{
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}
//in case the above method doesn't work, do it the following way:
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private var _frameCounter:Number = 0;
private function onEnterFrame(e:Event):void
{
_frameCounter++;
var desiredCount:Number = 1;//start with one - increment until it works.
if(_frameCounter < desiredCount)
return;
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}

Resources