Tom
Tom

Reputation: 8137

Howto extend Loader class and return cached assets in actionscript / flex 3?

I've been trying to create an universal asset loader class (with help of the folks here at stackoverflow), which remembers previousely downloaded assets by storing them in an associative array.

This is the end result:

AssetLoader.as

package
{
    import flash.display.Loader;
    import flash.events.Event;
    import flash.net.URLRequest;
    import flash.utils.ByteArray;

    public final class AssetLoader extends Loader
    {
        public static var storedAssets:Object = {};
        private var postUrl:String;
        private var urlRequest:URLRequest;
        private var cached:Boolean = false;

        public final function AssetLoader(postUrl:String):void
        {
            this.postUrl = postUrl;
            if (storedAssets[postUrl])
            {
                cached = true;
            }
            else
            {
                urlRequest = new URLRequest(Settings.ASSETS_PRE_URL + postUrl);
                contentLoaderInfo.addEventListener(Event.COMPLETE, OnAssetLoadComplete);
            }
        }

        //starts loading the asset
        public final function loadAsset():void
        {
            if (cached)
            {
                loadBytes(storedAssets[postUrl]);
            }
            else
            {
                load(urlRequest);
            }
        }

        //runs when the asset download has been completed
        private final function OnAssetLoadComplete(event:Event):void
        {
            storedAssets[postUrl] = contentLoaderInfo.bytes;
        }
    }
}

Settings.ASSETS_PRE_URL equals "http://site.com/assets/"

Now, my problem is that it is causing the client to crash whenever it tries to retrieve the caches version (the newly downloaded one does work) from the class:

var assetLdr:AssetLoader = new AssetLoader("ships/" + graphicId + ".gif");
assetLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, onShipAssetComplete);
assetLdr.loadAsset();

private function onShipAssetComplete(event:Event):void
{
    var shipImage:Bitmap = Bitmap(event.target.loader.content);
    // Do stuff with shipImage
}

When the cached version is being loaded, I get the following error in dutch: "TypeError: Error #1034: Afgedwongen typeomzetting is mislukt: kan flash.display::MovieClip@5c13421 niet omzetten in flash.display.Bitmap. at GameShip/onShipAssetComplete()" - means something like "type convertion has failed, can not convert flash.display::MovieClip@... to flash.display.Bitmap".

So, I wonder, how should I extend this loader class and make it return a cached asset the right way? Is my way of storing the asset in the array invalid maybe? Or should I use something else than loadBytes in the AssetLoader method?

Upvotes: 1

Views: 4902

Answers (2)

Arthur Debert
Arthur Debert

Reputation: 10697

You are probably aware of that, but there is an open source library for as3 (bulkloader) that does that and much more. (shameless plug here, since I am the author.

At the very least, reading through the source code might give you ideas on issues to tackle and maybe some implementation pointers.

Cheers Arthur Debert

Upvotes: 1

James Fassett
James Fassett

Reputation: 41054

I'm not sure why you are insistent on using the contentLoaderInfo if you are busy encapsulating the functionality -- go ahead and encapsulate the data too. Also, why store the bytes for an object instead of a simple reference to the actual object?

Here is an example of what I mean. Take a look at the one degenerate case ... that is a request that could be cached but isn't because the laoder is in the process of loading ...

package 
{

import flash.display.BitmapData;
import flash.display.Sprite;

public class TestAssetLoader extends Sprite
{
    public var loader:AssetLoader;
    public var degenerateLoader:AssetLoader;
    public var cachedLoader:AssetLoader;

    public function TestAssetLoader()
    {
        loader = new AssetLoader("picasso_blue_guitar.jpg");
        loader.addEventListener(AssetLoaderEvent.ASSET_LOAD_COMPLETE, handleAssetLoaded);
        loader.loadAsset();

        // NOTE: you'll have to think about this case ....
        // where an asset is in the process of loading when you get another request
        // e.g. it isn't yet cached but is already being loaded ... 
        degenerateLoader = new AssetLoader("picasso_blue_guitar.jpg");
        degenerateLoader.loadAsset();
    }

    private function handleAssetLoaded(event:AssetLoaderEvent):void
    {
        // here is your content
        // var myImage:Bitmap = Bitmap(event.content);

        // This is guaranteed to hit the cache
        cachedLoader = new AssetLoader("picasso_blue_guitar.jpg");
        cachedLoader.loadAsset();
    }
}
}

The changed up asset Loader:

package
{
    import flash.display.Loader;
    import flash.events.Event;
    import flash.net.URLRequest;

    public final class AssetLoader extends Loader
    {
        public static var ASSETS_PRE_URL:String = "";

        public static var storedAssets:Object = {};
        private var postUrl:String;

        public final function AssetLoader(_postUrl:String):void
        {
                postUrl = _postUrl;
        }

        //starts loading the asset
        public final function loadAsset():void
        {
            if(storedAssets[postUrl])
            {
                trace("cached load");

                var resource:DisplayObject = storedAssets[postUrl];

                if(resource is Bitmap)
                {
                    resource = new Bitmap(Bitmap(resource).bitmapData);
                }

                dispatchEvent(new AssetLoaderEvent(AssetLoaderEvent.ASSET_LOAD_COMPLETE, resource));
            }
            else
            {
                var urlRequest:URLRequest = new URLRequest(ASSETS_PRE_URL + postUrl);
             contentLoaderInfo.addEventListener(Event.COMPLETE, OnAssetLoadComplete);
                 load(urlRequest);
            }
        }

        //runs when the asset download has been completed
        private final function OnAssetLoadComplete(event:Event):void
        {
            trace("non-cached load");
            var loader:Loader = Loader(event.target.loader); 
            storedAssets[postUrl] = loader.content;
            dispatchEvent(new AssetLoaderEvent(AssetLoaderEvent.ASSET_LOAD_COMPLETE, loader.content));
        }
    }
}

And the event:

package
{
import flash.display.DisplayObject;
import flash.events.Event;

public class AssetLoaderEvent extends Event
{
    public static const ASSET_LOAD_COMPLETE:String = "AssetLoaderEvent_LoadComplete";

    public var  content:DisplayObject;

    public function AssetLoaderEvent(type:String, _content:DisplayObject, bubbles:Boolean=false, cancelable:Boolean=false)
    {
        content = _content; 
        super(type, bubbles, cancelable);
    }

    override public function clone():Event
    {
        return new AssetLoaderEvent(type, content, bubbles, cancelable); 
    }

    override public function toString():String
    {
        return "[AssettLoaderEvent] " + type;
    }
}
}

Upvotes: 3

Related Questions