The right way to do a preloader in AS3

I wanted to write this post for a very long time but Lee Brimelow beat me to it. But that is alright because he confirmed that the way I was doing my preloaders was a good one.

In AS2 we were used to seeing preloaders in the 2 first frames of a flash movie, but that technique didn't work well when you used Classes and Library object exported for ActionScript. Even in AS2, I started making my preloader external. An external preloader is a swf that sole purpose is to load another swf.

I suggest you watch Lee's video because it explains very well why you should use an external preloader and how to do it.

I think his method can be perfected to be even more memory and CPU efficient.

Let's take a look at the code he uses:

Actionscript:
  1. var l:Loader = new Loader();
  2. l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop);
  3. l.contentLoaderInfo.addEventListener(Event.COMPLETE, done);
  4. l.load(new URLRequest("content.swf"));
  5.  
  6. function loop(e:ProgressEvent):void
  7. {
  8. var perc:Number = e.bytesLoaded / e.bytesTotal;
  9. percent.text = Math.ceil(perc*100).toString();
  10. }
  11.  
  12. function done(e:Event):void
  13. {
  14. removeChild(percent); // was removeChildAt(0) but it make more sense this way
  15.  
  16. percent = null;
  17. addChild(l);
  18. }

That's pretty simple and straight forward. Lee makes sure to remove assets he used for is preloader to free memory, which is good,  but if you really wanted to push things a bit farther here is what you would do:

Actionscript:
  1. var myMovie:Sprite;
  2.  
  3. var l:Loader = new Loader();
  4. l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop, false, 0, true);
  5. l.contentLoaderInfo.addEventListener(Event.COMPLETE, done, false, 0, true);
  6. l.load(new URLRequest("content.swf"));
  7.  
  8. function loop(e:ProgressEvent):void
  9. {
  10. var perc:Number = e.bytesLoaded / e.bytesTotal;
  11. percent.text = Math.ceil(perc*100).toString();
  12. }
  13.  
  14. function done(e:Event):void
  15. {
  16. removeChild(percent); // was removeChildAt(0) but it make more sense this way
  17. percent = null;
  18.  
  19. myMovie = Sprite(l.content);
  20.  
  21. addChild(myMovie);
  22.  
  23. l..contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, loop);
  24.  
  25. l..contentLoaderInfo.removeEventListener(Event.COMPLETE, done);
  26.  
  27. l = null;
  28.  
  29. }

First difference you might find is that I set the weakReference of the listeners to true. It doesn't serve any purpose in this case but I think it is a good pratice and a good habit to always put weakReference to true for you listeners.

The second thing you might see is that I don't add the Loader to the stage. I don't want to do that because I want to get rid of the Loader. It did it's job, now I don't want it using memory for nothing. So instead I created a Sprite called myMovie and I give it the content of the Loader. For this to wor, the content of the Loader as to be a Sprite, but you can always change the type of myMovie to adapt to what you are loading. Once I gave myMovie the content I add it to the stage.

Now that the Loader handed off its package I can get rid of it. The first step to do so is to remove the listeners. Listeners take up CPU ressources and in this case they will never be used again so I might as well remove them. Once this is done I set the Loader to null to ged rid of it. I could have just done that and not remove the listeners before because I used weakReferences and the GarbageCollector would have destroyed them, but why wait until then when you can do it now.

Well that was it, it is really just a little cleaner but I think that if someone was looking to push this further that's how he would do this.

Do you know any better way of preloading?

17 Responses to “The right way to do a preloader in AS3”

  1. Theo Says:

    One interesting aspect of using a separate SWF for preloading is that it completely removes what little possibility there was that Google would index your Flash site, at least until they start indexing dynamically loaded content.

    I’m not saying you shouldn’t use this preloading/bootstrapping technique, on the contrary, I think it’s great. What I am saying is that Google’s new Flash indexer is completely useless, because it doesn’t take in consideration the way Flash developers actually create Flash content. This is an example of that.

  2. funkyboy Says:

    To help garbage collection even more, I’d move up removeListener() to line 16.

  3. doug Says:

    Nice idea but shouldn’t you change this:

    l.removeEventListener( ProgressEvent.PROGRESS, loop);
    l.removeEventListener(Event.COMPLETE, done);

    to

    l.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, loop);
    l.contentLoaderInfo.removeEventListener(Event.COMPLETE, done);

  4. doug Says:

    Here’s my example:

    package com.deceptiveresolution.kohive.swf.preloading
    {
    import flash.text.TextField;
    import flash.events.ProgressEvent;
    import flash.events.Event;
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.display.MovieClip;
    import flash.display.Sprite;

    public class PreloadingContainer extends MovieClip
    {

    private var __container:Sprite;
    private var __preloader:Loader;
    public var percent_txt:TextField;
    private static const __APP_URL:String = “TextChat.swf”;

    public function PreloadingContainer()
    {
    trace(”\nPreloadingContainer()”);
    __preloader = new Loader();
    __preloader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _onPreloadProgress, false, 0, true);
    __preloader.contentLoaderInfo.addEventListener(Event.INIT, _onPreloadInit, false, 0, true);
    __preloader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onPreloadComplete, false, 0, true);
    __preloader.load(new URLRequest(__APP_URL));
    }

    private function _onPreloadProgress(e:ProgressEvent):void
    {
    trace(”\nPreloadingContainer._onPreloadProgress()”);
    var percentage:Number = e.bytesLoaded / e.bytesTotal;
    percent_txt.text = String(Math.ceil(percentage * 100));
    }

    private function _onPreloadInit(e:Event):void
    {
    trace(”\nPreloadingContainer._onPreloadInit()”);
    percent_txt.text = “100″;
    }

    private function _onPreloadComplete(e:Event):void
    {
    trace(”\nPreloadingContainer._onPreloadComplete()”);
    removeChild(percent_txt);
    percent_txt = null;
    __container = Sprite(__preloader.content);
    addChild(__container);
    __preloader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, _onPreloadProgress);
    __preloader.contentLoaderInfo.removeEventListener(Event.INIT, _onPreloadInit);
    __preloader.contentLoaderInfo.removeEventListener(Event.COMPLETE, _onPreloadComplete);
    __preloader = null;
    }

    }

    }

  5. doug Says:

    @Theo

    Another good point but we could just have a load of static text in our preloading swf describing our site with perhaps links to more detailed external assets… just an idea for now.

  6. zedia.net Says:

    @Theo
    You are totally right, but as you say I don’t think it is of any use to try to make your SWF searchable for Google. Progressive enhancement is still the way to go.

  7. zedia.net Says:

    @Doug
    Thx for pointing out my error on the remove listener. It is fixed now.

  8. External Preloader; more complex cases | preloading in as3 | zedia flash blog Says:

    […] zedia flash blog Flash, ActionScript, SEO and everything in between « The right way to do a preloader in AS3 […]

  9. barry Says:

    HI,

    I’ve read a lot of articles that suggest you should set use weak reference to true. Is there any instance when you might want it to be false?

  10. zedia.net Says:

    @barry
    The only case where you would want weak reference set to false on a listener is when the only thing that keeps the object, that the listener is on, from being Garbage Collected is the listener itself. And I don’t see any application of this so I always set the weak reference to true.

  11. Tunde Says:

    please go to 8landphoto.com, click on work and you should see my problem is there a step by step way to fix this?

  12. panorama360 Says:

    very usefull, it saved me a lot of time! thanx

  13. Garfty Says:

    It’s nice to see this, and it’s the way i’ve been doing it after reading various articles etc etc…

    My only question is how would you do a nice dynamic loader, one that fades in, does the load, then fades out to reveal the content. There is a big spaceship version which is nice, however I feel this is quite complex still.

    Cheers for the post, cleared a couple of niggly things up for me

    ^_^

  14. Sharpy Says:

    This works well BUT … content.swf starts playing BEFORE event.COMPLETE fires! So by the time your progress hits 100%, your main file content is many frames into the animation.

    Any ideas how to stop/pause the content.swf until its fully loaded?

  15. zedia.net Says:

    @sharpy
    Put a stop on the first frame of the movie that is being loaded and in the onComplete function of your preloader start the content. Using the code I have written up there it should look something like this:

    myMovie = MovieClip(l.content);
    addChild(myMovie);
    myMovie.play();

    In my exemple the loaded content was a Sprite but you would have to change it to MovieClip to make your thing work.

  16. John Smith Says:

    I took this concept and added to it. I am building a thumb panel, so I wanted all of my thumbnail urls to be held externally in an XML file. Basically what this does is loads a series of .jpgs of .swfs and increments the x coordinate of the dynamic text field everytime i changes. Here is my code. I am just learning how to garbage collect, so I would appreciate if you would look at my code and critique it.

    var xml:XML;
    var xmlPath:String = “./Gallery1/gallery.xml”;
    var imagelist:XMLList;
    var numberOfImages:int;
    var thumbUrl:String;
    var thumbx:Number=0;
    var i:Number=0;
    var ihaschanged:Boolean = false;
    var thmbLoader:Loader = new Loader();

    var loader = new URLLoader();
    loader.load(new URLRequest(xmlPath));
    loader.addEventListener(Event.COMPLETE, xmlLoaded);

    //create a dynamic preloader text field
    var preloaderText = new TextField();
    preloaderText.name = ‘textfield’;
    preloaderText.selectable = false;
    preloaderText.x = 27;
    preloaderText.y = 20;
    preloaderText.width = 30;
    var txtFormat:TextFormat = new TextFormat();
    txtFormat.color = 0×000000;
    txtFormat.size = 12;
    preloaderText.setTextFormat(txtFormat);
    addChild(preloaderText);

    function xmlLoaded(e:Event):void {
    xml = new XML(e.target.data);
    imagelist = xml.gallery.image;
    numberOfImages = imagelist.length();
    createThumbnail();
    }

    function createThumbnail():void{
    if(i

  17. John Smith Says:

    looks like the bottom half got cutt off so here is the rest of it…

    function createThumbnail():void{
    if(i

Leave a Reply


Close
E-mail It