URLLoader subclass with automatic refresh support
Chris Hayen tweeted to me this morning expressing his wish that the ActionScript 3 URLLoader class had a refreshInterval
property. I responded that this should be pretty simple to add by extending the class. Well, I had a little time while I download some internal software builds, so I put together a class that adds a refreshInterval
property.
This has not been tested very much. In fact, I have only tested when the data loads successfully from the server. However, I wanted to post an early / rough version here with the hope that I get feedback on it. If the consensus is that this could be useful, then I will look at making it more solid, and adding it to the as3corelib library.
The class adds two new public APIs:
- refreshInterval Number is milliseconds on how often the data should be loaded.
- callIsActive Boolean that indicates whether the instances is currently in the process of loading data from the server.
Here is the class:
package
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Timer;
/*
* Class that adds an option to automatically have the data reloaded at
* specified intervals.
*/
public class URLLoader2 extends URLLoader
{
private var _refreshInterval:Number = 0;
private var timer:Timer;
private var lastRequest:URLRequest;
//make this public
private var _callIsActive:Boolean = false;
/*
Constructor
*/
public function URLLoader2(request:URLRequest=null)
{
//store last request used
lastRequest = request;
//register for events
addListeners();
//call base class constructor
super(request);
}
/********** public setters / getters **************/
/*
Refresh interval in milliseconds.
If equal to or less than 0, data will not be reloaded.
note: currently refresh timer starts from the time that the request
goes to the server, not from when the request changes. Should this
change?
*/
public function set refreshInterval(value:Number):void
{
_refreshInterval = value;
}
public function get refreshInterval():Number
{
return _refreshInterval;
}
//read only, whether the class is currently in process of loading data
//from server
public function get callIsActive():Boolean
{
return _callIsActive;
}
/************* public methods ***************/
//overriden load method
public override function load(request:URLRequest):void
{
//might need to listen for some of the status codes
stopTimer();
//store the last request so we can reuse it
lastRequest = request;
//call load to make the request
super.load(request);
//start the timer for the reload
startTimer();
//set that the class is in the process of communicating with the server
_callIsActive = true;
}
//overriden close method
public override function close():void
{
stopTimer();
_callIsActive = false;
super.close();
}
//private api to stop the timer
private function stopTimer():void
{
if(timer != null)
{
//clear the timer. We need to do this instead of reuse it
//in case the refreshInterval changes the next time we use it.
//although we could potentially check that when we start the
//timer
timer.stop();
timer == null;
}
}
//private api to start the timer
private function startTimer():void
{
//make sure the timer is stopped firest
stopTimer();
//if call to server is active, then dont refresh
//should we queue this up?
//if refresh interval is less than or equal to zero dont refresh
if(_refreshInterval <= || _callIsActive)
{
return;
}
//create new timer instance
//note, we could check if the existing timer instance can be used
timer = new Timer(_refreshInterval);
timer.addEventListener(TimerEvent.TIMER, onTimer);
//start the timer
timer.start();
}
//adds listeners for loading events
private function addListeners():void
{
addEventListener(Event.COMPLETE, onComplete);
addEventListener(IOErrorEvent.IO_ERROR, onIOError);
addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
}
/*********** private event handlers ******************/
//event hander when timer interval is fired
private function onTimer(e:TimerEvent):void
{
//callIsActive should never be true here, but should we check it
//anyways?
//load the last request
load(lastRequest);
}
//called when data is succesfully loaded
private function onComplete(e:Event):void
{
_callIsActive = false;
startTimer();
}
//called if there is an error loading data
private function onIOError(e:IOErrorEvent):void
{
_callIsActive = false;
startTimer();
}
//called if there is a security error loading data
private function onSecurityError(e:SecurityErrorEvent):void
{
_callIsActive = false;
startTimer();
}
}
}
Here is a simple example of using the class (it is pretty much the same as using URLLoader
):
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLRequest;
public class URLLoaderRefreshTest extends Sprite
{
public function URLLoaderRefreshTest()
{
var r:URLRequest = new URLRequest("http://feeds.feedburner.com/MikeChambers/");
var u:URLLoader2 = new URLLoader2();
u.addEventListener(Event.COMPLETE, onComplete);
u.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
u.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
u.refreshInterval = 3000;
u.load(r);
}
private function onComplete(e:Event):void
{
trace("oncomplete");
}
private function onIOError(e:IOErrorEvent):void
{
trace("onioerror");
}
private function onSecurityError(e:SecurityErrorEvent):void
{
trace("onsecurityerror");
}
}
}
Couple of notes:
- The class needs a better name. Any suggestions?
- Class is currently in default package
- Currently, the refresh countdown starts at the beginning of the data load, and not the end. Thoughts?
- Even if
refreshInterval
is 0 and refreshing not being used, I keep references to data loading events. This is small bit of extra overhead (in cases where data is not being refreshed), but simplifies the code. - If a refresh interval is reached and the instance is still communicating with the server, the refresh will be skipped.
- Class has had very little testing, especially in cases where an error occurs from loading the data (so there are probably some bugs and logic errors).
So, if you have any suggestions for improvements, find any bugs, or think it is (or isnt) useful, post them in the comments.