12

URLLoader subclass with automatic refresh support

 3 years ago
source link: http://www.mikechambers.com/blog/2008/09/12/urlloader-subclass-with-automatic-refresh-support/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

URLLoader subclass with automatic refresh support

Friday, September 12, 2008

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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK