AS3: Reusing URLLoader, URLRequest, and URLVariables instances - actionscript-3

My Flash project uses URLLoader.load a lot to load content from a web server and post to a php page. Should I reuse my URLLoader, URLRequest, and URLVariables instances, or should I create new ones each time? If I should create new ones each time, do the old ones need to be disposed of somehow?

You should most certainly never ever ever reuse any instances related to external operations and you should thoroughly dispose of them the very moment you don't need them. The overhead of Garbage Collector (GC) working on these objects is literally nothing next to the nightmare mess you might get into once your external operations collide via sharing the same operational instances.
URLVariables and URLRequest do not need any special treatment, just set null to any variables referencing them and ensure that method, where they were assigned to local variables, do not produce any function closures. Well, set URLRequest.data to null to break this reference.
URLLoader, on the other hand, needs to be pushed around a bit:
If URLLoader.data is a ByteArray, then you should ByteArray.clear() it (unless you need it).
Set the URLLoader.data to null.
Initially subscribe all error handlers with weak references (fifth argument of addEventListener set to true) and don't unsubscribe them. Weak keys won't affect the GCs judgement while keeping the subscriptions might save you from occasional Unhandled Error Event case.
Certainly do unsubscribe all non-error handlers.
In all the handlers, first check if Event.target is a valid URLLoader instance to avoid handling an event from a dead/disposed URLLoader.
Call URLLoader.close() just in case. Yes, after all of above is done.
Below is the class I use to load things in a simple way. It is built on the same principles I listed above. It allows loading text/binary data and also provides some proof against unstable network: you can set the repeatCount argument to higher values to provide fail-safe loading if you know that requests tend to fail sometimes.
Usage:
// Load binary data over unstable network.
DataFiles.load("data.dat", onData, true, 10);
// Load XML file as text over a stable network or from the local storage.
DataFiles.load("setup.xml", onSetup);
function onData(source:ByteArray):void
{
if (!source)
{
// Loading failed. Error case.
}
else
{
// File is loaded normally.
}
}
function onSetup(source:String):void
{
try
{
var aSetup:XML = new XML(source);
// Process loaded XML normally.
}
catch (fail:Error)
{
// The source is either null or an invalid XML string.
// Loading is failed, basically. Error case.
}
}
Implementation:
package simplify
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;
public class DataFiles
{
static private var list:Vector.<DataFiles> = new Vector.<DataFiles>;
static public function load(url:String, handler:Function, binary:Boolean = false, repeatCount:int = 1):void
{
var aLoader:DataFiles = new DataFiles;
aLoader.url = url;
aLoader.binary = binary;
aLoader.handler = handler;
aLoader.repeatCount = repeatCount;
list.push(aLoader);
aLoader.start();
}
private var url:String;
private var binary:Boolean;
private var handler:Function;
private var loader:URLLoader;
private var repeatCount:int;
private function start():void
{
loader = new URLLoader;
if (binary) loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onComplete);
loader.addEventListener(IOErrorEvent.IO_ERROR, onError, false, 0, true);
loader.load(new URLRequest(url));
}
private function destroyLoader():void
{
if (!loader) return;
loader.removeEventListener(Event.COMPLETE, onComplete);
var aDead:Loader = loader;
loader = null;
aDead.data = null;
aDead.close();
}
private function onComplete(e:Event):void
{
if (e.target != loader) return;
var aResult:* = loader.data;
var aHandler:Function = handler;
destroy();
destroyLoader();
aHandler(aResult);
}
private function onError(e:IOErrorEvent):void
{
if (e.target != loader) return;
destroyLoader();
repeatCount--;
if (repeatCount >= 0)
{
start();
}
else
{
var aHandler:Function = handler;
destroy();
aHandler(null);
}
}
private function destroy():void
{
var anIndex:int = list.indexOf(this);
if (anIndex > -1) list.splice(anIndex, 1);
handler = null;
url = null;
}
}
}

Related

It's possible to write a sharedobject when allowscriptaccess is = "never"?

I will try to write a sharedObject item using my swf embeded into an html page.
"allowscriptaccess" is setted to "never". I can't write sharedObject!
However if I set allowscriptaccess to "always", write action work well...
If I can't use sharedObject with allowscriptaccess setted to never, exist alternative for saving data?
I write a little and stupid example:
public class Main extends Sprite {
private var SHARED_NAME:String = "__SO__";
private var so:SharedObject;
Security.allowDomain("*");
Security.allowInsecureDomain("*");
public function Main() {
this.so = SharedObject.getLocal(this.SHARED_NAME, "/");
this.setSharedObj("YEAHHHHHHHH");
this.getSharedObj();
}
public function getSharedObj(clientId:String = null):Object {
var url:String = "http://localhost:8080?so=" + this.so.data.test;
var request:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.load(request);
return this.so.data;
}
public function setSharedObj(setValue:String):void {
this.so.data.test = setValue;
this.so.flush();
}
}
When embed the compiled swf using AllowScriptAccess: "never", get Request never have setted the queryparam so.
If set AllowScriptAccess to "always" queryparam will be correctly setted
#akmozo is right, the problem was not related by AllowScriptAccess, but from my way of testing.
The problem is:
Using firefox in anonymous mode, every time you reload the page, shared object are deleted.
I thought for anonymous session are saved, it is not so!

as3 return value on loader complete

I am trying to get my function to return a value from a php script once it has loaded. I am having issues with the 'return' aspect. Trying to the the value of 'TheLink" from a function with a 'return' in it. It's usually null. What am I doing wrong here?
var theLink = loadAudio();
public function loadAudio():String
{
var req:URLRequest = new URLRequest("myScript.php");
var loader:URLLoader = new URLLoader(req);
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE, Finished);
function Finished(e:Event)
{
var theValue = JSON.parse(e.target.data.audioLink);
return theValue;
}
}
A lot of things are wrong in your code but more important is that you cannot escape the asynchronous nature of AS3.
First thing first, even if AS3 wasn't asynchronous your code would still not work. The "loadAudio" never returns anything so theLink can never get any data. The 'Finished' method is the one return something but there's no variable to catch that data either.
Now the real stuff, AS3 is asynchronous that means that things will happen at a later time and this is why you need to pass an event listener in order to 'catch' when things happen. In your case the Event.COMPLETE will trigger at a later time (asynchronous) so there's no way for a variable to catch the result before the result is actually available so:
var theLink:Object = loadAudio();//this is not possible
The correct way is:
private var loader:URLLoader;//avoid GC
private var theLink:Object;//this will store result when available
//I assume you are using a class
public function MyClass()
{
loadAudio();//let's start loading stuff
}
private function loadAudio():void
{
var req:URLRequest = new URLRequest("myScript.php");
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE, handleLoaded);
loader.load(req);
}
private function handleLoaded(e:Event):void
{
theLink = JSON.parse(e.target.data.audioLink);
//done loading so store results.
}

Returning a string in Action Script 3.0

I'm really not familiar with Action Script 3 at all but I am with other languages.
I'm hoping someone could help me.
I'm attempting to make a modification to JWplayer so that an rtmp stream is retrieved via a PHP script rather than it being supplied in the HTML.
The code I currently have is below:
function useData(event:Event):void {
var data:String = event.target.data.toString();
}
/** Load content. **/
override public function load(itm:PlaylistItem):void {
_item = itm;
_position = 0;
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, useData);
loader.load(new URLRequest("http://192.168.0.12/phpauth/play1.php"));
// Set Video or StageVideo
if(!_video) {
_video = new Video(320, 240);
_video.smoothing = true;
_video.addEventListener('renderState', renderHandler);
// Use stageVideo when available
if (_stageEnabled && RootReference.stage['stageVideos'].length > 0) {
_stage = RootReference.stage['stageVideos'][0];
_stage.viewPort = new Rectangle(0,0,320,240);
_stage.addEventListener('renderState', renderHandler);
}
attachNetStream(_stream);
}
// Load either file, streamer or manifest
if (_item.file.substr(0,4) == 'rtmp') {
// Split application and stream
var definst:Number = _item.file.indexOf('_definst_');
In the load function the file name to play is held in _item.file. I'm trying to make a call to a php script which then overwrites the value in _item.file. I've confirmed that the php is being called but I don't know how to get the data from the data string in the useData function into the _item.file string.
Any help would be really appreciated - I suspect this is a simple one but my lack of AS3 knowledge is making it really difficult.
Thanks,
Your problem basically about how to access a local variable in an event handler. A quick and dirty way can be to have an anonymous function used as a handler like:
loader.addEventListener(Event.COMPLETE, function(event:Event):void {
var data:String = event.target.data.toString();
_item.file = data;
});
This approach would work, because this anonymous function has access to the local variables inside load function as is. But, you need to be cautious that the anonymous function uses the variable exactly as the calling function is using. So, let's say there is a loop in load function and _item changes in every iteration of the loop. For that scenario, when load handler gets called, its _item would also have changed to the object which was last assigned to _item.
A far cleaner and OO approach can be to have a handler class like:
package {
public class LoadHandler {
private var _item:PlaylistItem;
public function LoadHandler(item:PlaylistItem) {
_item = item;
}
public function loadHandler(event:Event):void {
var data:String = event.target.data.toString();
_item.file = data;
}
}
and then have loader.addEventListener(Event.COMPLETE, (new LoadHandler(_item)).loadHandler). Hope that helps. BTW, LoadHandler could be made more generic to take and array of objects to be used and a callback function. loadHandler function, then could just call callback function with that array of objects.
If you are returning a simple string from PHP you should be able to use
event.target.data;
e.g. from PHP... echo "hello";
var data:String = event.target.data
You could try tracing the response to ensure you are getting something back from PHP.
You can either test this from within the IDE or install the Debug version of the Flash Player browser plugin.
trace("Response from PHP: "+event.target.data);
_item.file = event.target.data;
trace("_item.file: "+_item.file);

connection between Flex and FLash

I am working on a project, which is based on two main parts, the first part is done by Flex, and second one is a flash professional project, contains PROJECTNAME.fla and PROJECTNAME.as files. My question is how we can set some parameters in .fla project (e.g. usernames, user's images) from flex part. I explain main procedure by following;
Connect to server by flex part and get user's status
run .swf created by a flash professional project as described above
set some parameters in .swf file.
I have googled a lot, and I did not find any solution. (there was some solution that converts symbol to flex component, since it works for converting a single symbol). Any Idea will be appreciated.
There are several possibilities to pass the params from one swf (flex in your case) to another runtime loaded fla.swf:
1.Pass through the loading query params:
code in flex.swf:
public function astest()
{
var loader:Loader = new Loader();
addChild(loader);
loader.load( new URLRequest("astest1.swf?param1=value1&param2=value2"));
}
access params from fla.swf:
public function astest1()
{
if(stage)
onAdded();
else
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
protected function onAdded(event:Event = null):void
{
//root.loaderInfo.parameters - params of this swf file
//stage.loaderInfo.parameters - params of core swf file
var params:Object = root.loaderInfo.parameters;
for (var param:String in params)
trace(param,"=",params[param]);
}
output:
param2 = value2
param1 = value1
lacks of this method:
-one time usage, you can pass params only one time when loading
-the second swf must be runtime loaded by url, you can't embed it (or one of the class withing it) for example.
2.Runtime communication through the events
I recommend to use this method, it hasn't lacks of previous one.
Example of using stage as the global common dispatcher.
flex.swf:
public function astest()
{
addEventListener("ready", onReady);
var loader:Loader = new Loader();
addChild(loader);
loader.load( new URLRequest("astest1.swf"));
}
protected function onReady(event:Event):void
{
sendParams("param1=value1&param2=value2");
}
protected function sendParams(params:String):void
{
stage.dispatchEvent(new DataEvent("params", false, false, params));
}
fla.swf:
public function astest1()
{
if(stage)
onAdded();
else
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
protected function onAdded(event:Event = null):void
{
stage.addEventListener("params", onParams);
//fire event with bubbling that anables handling it in the parent swf
dispatchEvent(new Event("ready", true));
}
protected function onParams(event:DataEvent):void
{
var data:String = event.data;
trace(data);
}
output:
param1=value1&param2=value2
with this approach you send as many params as you need, you alsa can create custom event to pass Object parameters but in this case both project must have this cusom event in there source paths.

Loading and Returning Text Data with ActionScript 3 (URLLoader)

I'm trying to do something exceedingly simple: write a function that reads text from a text file and returns the text in a string using AS3.
The Function
public function readData(path:String):String
{
var dataSet:String;
var urlRequest:URLRequest = new URLRequest(path);
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete);
urlLoader.load(urlRequest);
function urlLoader_complete(evt:Event):void {
dataSet = urlLoader.data;
trace(dataSet)
}
trace(dataSet);
return dataSet;
}
Calling the Function
var dataString:String = aq.readData("http://example.com/data.txt");
trace(dataString);
This code returns a null string when I run it. Why?
EDIT:
Ok, I now see that this doesn't work because urlLoader is acting asynchronously. I'm writing a program that reads in a data file and acts on it. Does this mean that I need to write the rest of my program inside function urlLoader_complete? Or should I pause the program until urlLoader is finished?
In Flash and Flex, all network I/O is asynchronous. It has to be this way in order to avoid blocking your browser.
As a result, it is not possible to write a readData() function that directly returns the result of a network read operation. You will have to pass a callback function to the readData() function. When readData() has finished reading the data, it can call the callback function.
For example:
/**
* Asynchronous function to read data as a string. When the data has been read,
* the callback function is called with the result.
*
* #param path the URL to read
* #param callback the function that is called with the result; should take
* one string argument.
*/
public function readData(path:String, callback:Function):void
{
var dataSet:String;
var urlRequest:URLRequest = new URLRequest(path);
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete);
urlLoader.load(urlRequest);
function urlLoader_complete(evt:Event):void {
dataSet = urlLoader.data;
trace(dataSet);
callback(dataSet);
}
}
Here is how you might call that function from Flex:
<mx:Label id="mylabel" />
<mx:Button click="readData('http://www.google.com/',
function(s:String):void {mylabel.text = s})" />
Its been 3 years ago since this question arose with you, but since I stumbled on this problem a few hours ago, and managed to get it to work and thought why not share it. There might be better alternatives already, but hey I just started coding Actionscript so no blames :)
First build a Preloader class with a filecounter. There will be a numFiles parameter in the constructor which holds the total number of files to be loaded. Every time when the complete method is called, 1 to the filecounter will be added and a statement will be checking if all files are loaded. when the numFiles is equal to the counter call the start() method of the calling class.
*Preloader.as *
package
{
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class Preloader
{
public var urlLoader:URLLoader;
public var response:Array = new Array();
public var callingClass:Object;
public var numFiles:uint;
private var counter:uint;
public function Preloader(callingClass:Object, numfiles:uint)
{
this.callingClass = callingClass;
this.numFiles = numFiles;
}
public function load(name:String):void
{
var request:URLRequest = new URLRequest(name);
urlLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onLoad);
urlLoader.load(request);
}
public function onLoad(event:Event):void
{
response[counter] = event.currentTarget.data;
if(numFiles == counter) {
callingClass.start();
} else {
counter++;
}
}
}
}
The constructor method in the calling class will have to call all the preload files and the start method will be the replacement of your constructor stuff. note that when the preloader loads it need the reference to its calling class and the total number of "to be loaded" files:
package
{
import flash.display.MovieClip;
import misc.Preloader;
public class Path extends MovieClip
{
public var preloader:Preloader = new Preloader(this, 3); //pass this class and the total number of files
public function Controller()
{
preloader.loadJSON('file1.js');
preloader.loadJSON('file2.js');
preloader.loadJSON('file3.js');
}
public function start():void
{
trace(preloader.response[0]); //Get first file contents
}
}
}