Skip navigation

Image caching with Flex 3

As always I will start with my examples because I know most people want to see a demo before reading about it. Both of these examples are identical except for one line of code which flags image caching on or off. I have added stats.as so you can see the memory usage as you load more images. You will notice that when using the example with image caching you can load hundreds of images with little memory impact and it is much faster than the example with out caching. Also if you do not believe stats.as open up your task manager and view the memory usage of your browser to confirm that ImageCache is really working.
View source is enabled so you can see whats going on.

App NOT using cache
App using cache

I have seen several Flex 3 image caching solutions out on the web but most of them seemed to subclass SWFLoader or Image. In my opinion the Image class should know nothing about caching, it should just know about displaying the image. Why add extra functionality to the Image class? Why not have something else manage the caching? So I created ImageCache which is a utility class that handles caching images which can then be used as the source for any Image component.

The reason I like this approach is that it leaves the Image class alone and if you want to cache something you do not need to swap all the Images in your app with some subclass. You only need to change the way you set the Images source property.

I thought the browser cached images?

You may think that you don’t need to cache your images in Flex because your browser is already doing that. While it is true that your browser is caching the images that your Flex app requests it probably works a little different then you think. For example the first time you load an image you might notice that it takes a second, but if you load that same url again it shows up right away. This must mean that it is using a cached version of the image right? Well thats partly correct your browser is giving you a cached version of the image BUT, your Flex app is creating a whole new instance of that image data. So by default you get a speed gain with your browsers cache but what you are not getting is any memory gains. Each time you load that image your Flex app’s memory usage will go up by the size of that image. This is because each Image component keeps a separate BitmapData for its own use. In some cases this is good but most of the time it is not necessary and can be wasteful.

If we cache the images in the app it self we can achieve even faster loading speeds because we do not even need to ask the browser for the image. Second and even better, we can reuse the same image over and over with almost no memory overhead. This is done by sharing the same BitmapData between multiple Image components.

How it works

ImageCache works by keeping a mapping between URL and BitmapData. When you use ImageCache to cache an image it will load the image and then store the BitmapData for that image. Then when you want to set an Image component’s source to that image ImageCache will create a new Bitmap using a reference to the stored BitmapData. This means that there is almost no memory overhead and the image will load almost instantly because we are just referencing the BitmapData instead of creating a whole new copy of it. One thing to note is that because of the BitmapData sharing if you change the underlaying BitmapData of one of the Images all of the Images referencing that BitmapData will also change. This may or may not be good for you depending on how your app works.

ImageCache currently has 4 public methods:

public function setImageSource(image:Image, source:Object):void
public function cacheImage(source:Object):void
public function flushImage(source:Object):void
public function loadIntoCache(source:Object):void

The setImageSource method allows you to set an Image’s source using the cache. If the url is not cached already this method will cache the image and set the Image component’s source. If the image is already cached it will just set the Image component’s source.

Next the cacheImage method will simply cache an image with out setting any component’s source. This is useful for pre-caching images so they can be used later on with out having to load them.

If you want to flush an image from the cache you can use the flushImage method to do so. This method removes an image from the cache meaning that the next time it is requested it will ask the browser.

Lastly the flush method will clear all images in the cache.

When you use the setImageSource method you do not have to worry about timing or whether or not the image has been cached yet because ImageCache already accounts for that. It will keep a list of Image components that want a certain source and once it has finished caching it will go back and set it on all the Images that were waiting.

One more thing. I require Image components but that could easily be swapped out for SWFLoader or whatever class you wanted. This is just a quick example to show the benefits of using caching in your app. The same techniques could also be used to cache any type of file your app might be loading .swf, .pdf, .xml and so on.

Advertisements

4 Comments

  1. Very nice tutorial.

  2. Awesome! Thanks! Just started using this utility in a Flex 4 app and it works great! Would like to make a small contribution as there is one major issue with how you handle the waiting list.

    Needs to check if the Image component has already been added to the waiting list. This is critical because if not the same source is being loaded multiple times for the one instance of the Image component.

    I spotted this issue quickly as I’m using this utility for images in a virtualized List (setImageSource called when ItemRenderer’s data property is set). Adding in a trace statement at a critical point I noticed a lot of trace output when I shouldn’t have.

    You just need to make sure the Image component instance isn’t already in the waiting list. If its not, then add it, otherwise do nothing because if you do you would needlessly be reloading the same image source. A simple indexOf should suffice:

    private function addImageToWaitingList(image:Image, source:Object):void
    {
    var waitingList:Array = getWaitingListBySource(source) as Array;

    if (waitingList.indexOf(image) == -1)
    {
    waitingList.push(image);
    }
    }

    Any chance you will be at 360 Flex next week?

    • First off thanks for pointing this bug out and thanks even more for providing code to fix it. Secondly no 360 Flex for me this year, but I will be going to Adobe MAX in October. I can’t wait for that. Also I am glad that you where able to put this code to good use.

      I do have a newer version of this code which I am hoping to upload this weekend along with all my other odds and ends to a public repository, either GitHub or Google Code. Once I decide where I will let you know, and also update my articles with a link. That way you can get the newest versions and also commit your bug fixes if you want, or I can just work them into the code base.

  3. Not a problem Robert, happy to do my part! I enjoyed using the utility and wanted to give something back. But since then I’ve become much more familiar with the innards that I made further tweaks.

    I understand why you had the pendingImages array variable but felt it wasn’t necessary. Basically, what I did is removed the array, updated isCachePending to check against the waiting list and instead of adding the image to the waiting list first (when calling setImageSource method), the loadIntoCache method is called first. And since the image is added to the waiting list right after, the isCachePending simply returns true if the waiting list contains 1 or more images so the next time setImageSource is called for a different image but the same source, isCachePending returns true. Haha did you follow that?

    Since I moved code around to follow standards we have at my job the code has changed but would be happy to forward it to you.

    Ha, I’m used to going to Adobe MAX but this year going to 360 Flex instead.

    I highly recommend using GitHub! Please let me know as soon as the new code is up as you have my email from this post. Curious to know what other enhancements you’ve made. Thanks!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: