as3 air unloadAndStop() event listener or timer - actionscript-3

my air application plays an external swf over and over until time to play the next external swf. in order to avoid memory leaks i am using unloadAndStop(). i am using two timers. the first unloadAndStops the swf. the second waits two seconds then loads it back up again.
this approach (coupled with the use of weak references) seems to keep the memory in check. however, i'd rather not use timers but event listeners. is there an event listener for when unloadAndStop completes to then load the swf again.
here is what i had in mind:
var TIMER_INTERVAL:int = int(duration);
var t:Timer = new Timer(TIMER_INTERVAL);
t.addEventListener(TimerEvent.TIMER,updateTimer,false,0,true);
t.start();
private function updateTimer(e:TimerEvent):void
{
swfLoader.unloadAndStop(true);
swfLoader.addEventListener(Event.UNLOAD,onSWFUnloadComplete,false,0,true);
}
private function updateTimer(e:TimerEvent):void
{
var swfSource2:String = File.applicationStorageDirectory.nativePath.toString();
swfLoader.load(swfSource2+'\\'+name_xml);
}

unloadAndStop is not an asynchronous method, so an unload event wouldn't really be relevant. What is likely happening behind the scenes is that it takes 1 full frame to fully dispose of the movies objects/listeners and that's why you're having issues loading it again in the same block of code.
If you wait just one frame before loading it again, you should have the results you expect.
Now, of course the best solution is go into the source file of your loaded swf and fix the memory leaks.

Related

SWFLoader doens't work twice

I have a game that dynamicaly loads another minigame.
I load it using SWFLoader:
function loadMinigame(minigameName:String):void
{
var loader:SWFLoader = new SWFLoader;
loader.load("/Content/Swf/" + minigameName + ".swf");
layerMinigames.addElement(loader);
}
The first time I load it works nice. But when I exit the minigame and try to open it again via same method it doens't apper in my application (sometimes do sometimes doens't).
Both games are Flex application.
What's going on?
Update
I did loader.loadForCompatibility = true and now it loads properly! But now I'm getting an error when I try to cast the result:
loader.addEventListener(Event.COMPLETE, function(e:Event):void
{
var sys:SystemManager = SystemManager(e.currentTarget.content);
});
gives:
TypeError: Error #1034: Type Coercion failed: cannot convert
_AppMinigame1_mx_managers_SystemManager#14c97eb9 to mx.managers.SystemManager.
You'd better cache the loaded minigame after loading, then just display it again when needed. Make sure though, that the minigame's ADDED_TO_STAGE listener resets the minigame properly.
static var loadedGames:Object={};
function loadMinigame(minigameName:String):void
{
if (loadedGames[minigameName]==null) {
var loader:SWFLoader = new SWFLoader;
loader.load("/Content/Swf/" + minigameName + ".swf");
loadedGames[minigameName]=loader;
loader.addEventListener(Event.COMPLETE,loaderFinished);
} else {
layerMinigames.addElement(loadedGames[minigameName]);
}
}
function loaderFinished(e:Event):void {
layerMinigames.addElement(e.target);
// do the rest, like setting proper start time for minigame, etc
}
UPDATE: Since you run out of memory while running such a process, this means that the minigame's instance holds onto too much memory, and needs to be freed (GC'd) in order to load another minigame. Thus, caching loading SWFs is out of question. You can release an SWF using unloadAndStop() call against it, prior to doing this, make sure that your minigame is removed from display list, and its REMOVED_FROM_STAGE listener shuts down every listener attached to stage. Also you might need to debug your minigame SWF to locate probable memory leaks, as your SWFs share the same memory space, and a memory leak in one of them nullifies your efforts to do anything in all the others.

Waiting for a file to be loaded in Actionscript 3

I am reading files from disc that have information I need to display to the user. I set up an event listener that flags when the file is loaded- that's easy. However, that adds a delay before I can display the information, and it appears that doing a simple loop in the main code to wait for the file to be loaded does not work:
while (fileComplete == false);
Essentially that simply freezes the code- the file does not load while that loop functions. I can add a timer that checks every few milliseconds to see if fileComplete == true, but I'm wondering if there is a better way. Ideas?
Flash is by default single-threaded, this means if you're initiating an asynchronous process, you need to release the code flow for Flash engine to actually complete the process. Doing an infinite loop like yours does NOT release the code flow, so you should drop this idea. Instead, you should either blindly wait for Event.COMPLETE event and do the post-load actions in the listener, or you should listen for Event.ENTER_FRAME and check for the flag you're setting in Event.COMPLETE listener. The former approach is cleaner.

TimerEvent function ignoring Timer delay

I'm working in AS3, Flash AIR 3.2 for iOS SDK. I'm trying to run part of the program only after myLoader finishes loading an image. I have a myTimer.start(); which runs inside myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);.
What seems to be the problem at the moment is that the program is ignoring the 1000ms. The program is running after myLoader is finished at the moment, but it just seems to be doing its own thing in terms of the delay.
EDIT: Being more precise here... The program seems to be ignoring the Timer delay. Even if Timer is set to 100000ms. It seems to be running the rest of the program right after the image is loaded.
EDIT: I still had my methods running inside my Main() as well as the timerListener() in the code. Thought I commented them out. Whoops!
var myTimer:Timer = new Timer(1000);
public function Main()
{
init();
displayImage();
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timerDone);
}
public function displayImage():void {
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
myLoader.load(fileRequest);
}
public function onLoaderComplete(e:Event) {
//start Timer event here
myTimer.start();
}
public function timerListener (e:TimerEvent):void{
trace("Timer is Triggered");
myTimer.stop();
aMethod();
anotherMethod();
moreMethods();
}
You don't really clarify what you mean by "the timer is doing its own thing." Is the timer shorter or longer than you expect?
What I think is likely going on here is that your timer tick and your frame rate are out of synch. If you're familiar with the concept of the elastic racetrack, you know that the single-threaded nature of Flash (unless you're using worker threads) means that the screen can't update during a script and vice-versa. This means that if your Timer fires while the Display list is updating, it just has to wait until the display list finishes and may even have to wait until other scripts have run, depending on how Flash is prioritizing the different things in its queue.
From the Timer API:
Depending on the SWF file's framerate or the runtime environment
(available memory and other factors), the runtime may dispatch events
at slightly offset intervals. For example, if a SWF file is set to
play at 10 frames per second (fps), which is 100 millisecond
intervals, but your timer is set to fire an event at 80 milliseconds,
the event will be dispatched close to the 100 millisecond interval.
Memory-intensive scripts may also offset the events.

Modifying the domain memory of a loaded SWF to control behavior

I have several flash games that cannot be paused from within the games. I would like to write a wrapper swf that allows me to pause them. It is not feasible to recompile the games, so no functionality can be added to them.
What I would probably need to do is control when the loaded swf gets ENTER_FRAME events and redirect calls to flash.utils.getTimer to a function defined in the wrapper. Can this be done by modifying the domainMemory ByteArray of the loaded ApplicationDomain object?
domainMemory is definitely doesn't help you, it's just the API to access to the fast memory.
What you can try to do is to decompile the game swf and find the dispatcher of main EnterFrame event (and after loading game swf you have to find this dispatcher through the display list for example if it doesn't turned out to be the stage or root), that is used as the game tick dispatcher, than you will be able to intercept the default EnterFrame event for this dispatcher. For interception just add your own listener with the higher priority:
//pause the game
dispatcher.addEventListener(Event.ENTER_FRAME, onGameEnterFrame, false, 1);
//resume the game
dispatcher.removeEventListener(Event.ENTER_FRAME, onGameEnterFrame);
protected function onGameEnterFrame(event:Event):void
{
event.stopImmediatePropagation();
}

How do I completely remove a loaded swf and reload it? (Trying to restart a game)

I have a preloader that loads a swf, the swf creates a bunch of listeners, objects, and movie clips. I want all of these to be destroyed and recreated.
Simplified version of my preloader:
var request:URLRequest = new URLRequest("myfile.swf");
var myLoader:Loader = new Loader();
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, function(event){
stage.addChild(myLoader);
myLoader.loadBytes(urlLoader.data);
});
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(request);
When I try to remove it, I do this:
stage.removeChild(myLoader);
var child = loader.myLoader.content as Object;
SoundMixer.stopAll();
while(stage.numChildren > 0){
stage.removeChildAt(0);
}
child.stop();
while(stage.numChildren > 0){
stage.removeChildAt(0);
}
child=null;
System.gc();
myLoader.unloadAndStop(true);
System.gc();
myLoader.unload();
System.gc();
myLoader.loadBytes(urlLoader.data);
stage.addChild(loader.myLoader);
In your loaded SWF you may create a method 'destroy' which would remove all listeners, destroy all objects and reset all data.
You can call this method either from the parent object (if the method is public) or you can call destroy when you remove the SWF from stage (Event.REMOVED_FROM_STAGE)
You can reload swf with js, this is the easy way to do this. you can check this answer.
Or you have to do a good object and listener managment and reset game in swf file. This might be complex as project get bigger. You need release methods that removes all references and listener.
first you should load your swf using the Loader Class like this :
var movie:MovieClip
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load( new URLRequest("myfile.swf"));
function loadComplete(event:Event):void
{
movie = loader.content as MovieCLip;
addChild(movie);
}
//when reloading just remove the movie object from the stage
removeChild(movie);
SoundMixer.stopAll();
//......
//.
//.
I know this is old, but I stumbled on it for reference and see some things that I think should be mentioned.
System.gc() - while it's a function you have access to, everything I've seen suggests that the garbage collector should never be called by your code unless you've used a considerable amount of memory and need it back immediately. The garbage collector usually has a better idea of when it should run than you do (Force Garbage Collection in AS3?). It can impact performance for not just your application but all flash applications running in that browser or container.
There's also a good bit of people struggling to make effective use of unloadAndStop(), as it seems to be rather unreliable in various contexts (Unloading swf using the unloadAndStop() method, but video sounds remain audible).
To be thorough, I'd strongly suggest putting the effort into simply eliminating everything as it would be in any other language from within the loaded flash object first before removing the flash object itself, not expecting the container to take care of it for you. This is why Flash has a reputation for memory leaks to begin with.
Inside the loaded swf, add code that fires on unload.
One way is to add this to the swf:
this.addEventListener(Event.REMOVED_FROM_STAGE, doUnload)
Then have the doUnload functon perform the following:
Use removeEventListener to effectively terminate the event handlers. You could either recall all the addEventListeners you created, or cook up a routine to walk through and remove them all by traversing the objects in play. The first option is more efficient since it's exclusive, but the second option would theoretically be something you could memorize and copy between applications.
Use delete() on variables to make them removable by the garbage collector. NOTE: Unlike many languages, a referenced variable will not be removable unless the reference is also removed. For example:
var a:int = 0;
var b:int = a;
delete(a); // b still exists and equals 0, a is not available to GC.
delete(b); // now both variables can be removed by GC.
There's also still confusion as to whether it's a good idea to use removeChildAt(0) or remove the child objects individually. The first has the benefit of being distinctly simple and straight-forward, but that also gives it the caveat of not being entirely understood by the coder, and possibly missing something similarly to unloadAndStop(). Not as much as walking your object tree with removeChild and eliminating things explicitly without question or uncertainty.
After this is set, adding a function to unload the flash object will trigger its self-removal, so the loader will remain simple and neat, and reload the flash cleanly.