Flex Mobile Development: caching images using ContentCache

There are many places where you can optimize your code especially when working on mobile applications. In this post I will focus on caching images using the spak.core.ContentCache class (part of the Flex framework).

Right from the Flex documentation, you can see ContentCache could be useful:

Provides a caching and queuing image content loader suitable for using a shared image cache for the BitmapImage and spark Image components.

Why cache images?

So why would you cache images? Every time you set a new source for a Spark Image or a BitmapImage object a new flash.display.Loader object is created to load the image bytes.

Now, let’s suppose you display a list of items and each item has an image (could be an icon or could be a picture). Each image is hosted on a remote server so it has to be downloaded. Finally, some of these images are the same for different items in the list (for example for a Twitter client it is quite common to see many entries with the same avatar; each entry represents a tweet from the same user and the avatar is the same in this case).

If you don’t use a caching mechanism, you will notice that as you scroll through the list it will take some time to load images. And the same could happen for images stored locally especially if they are not very small. While waiting for images to load and be displayed the first time is OK (and there isn’t much to do other than preload them) waiting for the same image every time it is displayed is quite annoying.

Here comes the ContentCache class. This class stores the bytes loaded (actually it stores the LoaderInfo object used for loading each image) and every time an image must be loaded it first checks the cache; if is not there then it goes through the loading process. If it is cached already, then it provides the LoaderInfo object.

How do you use the ContentCache class?

You can enable caching for your Spark Image or BitmapImage objects quite easy. All you have to do is to create an instance of ContentCache and set this instance to the contentLoader property of the BitmapImage or Image object. Other than this, it’s business as usual. When you want to load an image you just set the source property of the Image or BitmapImage object and the ContentCache object will take care of the rest.

There is one thing you must be aware of: if an image was changed after it was cached and the source is the same then you have to invalidate the cache yourself in order to see the new image. Otherwise you will see the old version. When you want to remove an image from the cache you use the removeCacheEntry() method and pass the source for the image you want to invalidate in the cache.

You control the number of cached images using maxCacheEntries property (the default value is 100).

Let’s see a simple example:

private var cache:ContentCache = new ContentCache();

private function setImage(source:String):void {
   //if we didn't set the cache
   if ( !img.contentLoader ) {
       img.contentLoader = cache;
   }
   //set the image source
   img.source = source;
}
...
<s:Image id="img"/>

Using ContentCache with Lists

The Flex framework has a built-in list item renderer that supports images: IconItemRenderer. This is perfect for displaying lists that have images and text. By default the IconItemRenderer uses a ContentCache object to cache the images. However the ContentCache instance is not shared across all the item renderers.

What do I mean? A Flex mobile list uses virtualization. This means that if the list displays 10 items at once it will create about 11 item renderer instances and reuse them. So even if the list has 1,000 items to display only 11 item renderers will be created. Next, each item renderer instance will use a ContentCache instance to cache the images. So in total there will be 11 ContentCache instances. This means that it could happen to have an image already displayed by the list but because it was cached by a different ContentCache instance than the one used by the current item it will be loaded again.

The IconItemRenderer class uses a static property to hold an instance of the ContentCache. This means that the same image cache will be used by your list. However, if you have a second list or you use multiple item renderers for the same list, by default, it will be used another instance of ContentCache. The easiest way to fix this is to first create a single ContentCache instance (you can use the Singleton pattern here):

  1. package org.corlan.utils {
  2.         import spark.core.ContentCache;
  3.        
  4.         public class ImageCache {
  5.                
  6.                 private static var _instance:ImageCache;
  7.                
  8.                 public var cache:ContentCache;
  9.                
  10.                 public function ImageCache(s:Singleton, maxCacheEntries:int=100) {
  11.                         cache = new ContentCache();
  12.                         cache.maxCacheEntries = maxCacheEntries;
  13.                 }
  14.                
  15.                 public static function getInstance(maxCacheEntries:int=100):ImageCache {
  16.                         if (ImageCache._instance == null) {
  17.                                 ImageCache._instance = new ImageCache(new Singleton(), maxCacheEntries);
  18.                         }
  19.                         return ImageCache._instance;
  20.                 }
  21.         }
  22. }
  23.  
  24. class Singleton {}

Then you extend the IconItemRenderer and set this Singleton as the contentLoader object:

  1. public class ListItemRenderer extends IconItemRenderer {
  2.                
  3.                 public function ListItemRenderer() {
  4.                         super();
  5.                         iconContentLoader = ImageCache.getInstance().cache;
  6.                 }
  7. }

When you build from scratch a list item renderer that uses images, you can use the same approach.

Obviously this technique can be used for other places as well not only for lists.

Drawbacks

As with any caching or pooling technique there are trade-offs. You have to find a fine balance between not caching any images and caching all the images. By default the ContentLoader is set to cache 100 entries. But this doesn’t mean that it is the best value.  It all depends on how big the images are and how much memory you want to use for caching. So be sure you take this into consideration when caching images.

13 thoughts on “Flex Mobile Development: caching images using ContentCache

  1. Mihai, this is an excellent topic. Optimizing mobile apps is really important. I have one other image related problem.

    Recently I started developing Flex Android app for one jeans brand here in Croatia. I was wondering what to do when user starts an application – Should I store images and data in local Sql database so he doesn’t have to download data every time he starts an application on his phone?

    Do you have any other suggestion?

    Cheers,
    Ivan

  2. Hey Ivan,

    Long time no seen :)

    I think the best option for your use case is to save the data locally. You can use a mix of SQLite and files. Relational data -> SQLite, images -> in files (stored in application storage folder).

    I don’t see a better approach

    cheers,
    Mihai

  3. “By default the IconItemRenderer uses a ContentCache object to cache the images. However the ContentCache instance is not shared across all the item renderers.”

    Since the default ContentCache specified in IconItemRenderer.as is a static property called _imageCache, wouldn’t it be shared across all instances of IconItemRenderer? It just wouldn’t be shared across your IconItemRenderers and say, a custom MyImage component. What about when you extend IconItemRenderer, would the static _imageCache be shared between instances of iconItemRenderer and myIconItemRenderer?

    I definitely like the idea of a utils.ImageCache class. I currently have a LARGE_IMAGE_CONTENT_CACHE and a THUMBNAIL_CONTENT_CACHE in my utility class. I have been experimenting with what to set the maxCacheEntries. Right now I have the default 100 on the thumbnail one, and 20 on the large.

  4. Pingback: Weekly Developer Journal: August 14 – August 20

  5. Pingback: Cool Stuff with the Flash Platform - 8/19/2011 | Remote Synthesis

  6. @Jamie McDaniel

    You are right. It is shared across all the item renderer instances.

    Many thanks for pointing this out. I updated the post to reflect this.

    Mihai

  7. i want to handle situation that image does not exists on the server in my application.

    please can anybody help me:)

  8. Pingback: Image Preload - Flashforum

  9. Is it okay to use “const” instead of a “var”?

    private static const cache:ContentCache = new ContentCache();

    (it seems to work, but I wonder how does “const” apply here)

  10. Pingback: Optimize performance for Flex Mobile Apps « Stack & Heap

  11. Pingback: Optimize performance for Flex Mobile Apps | TechnoVeille

  12. Pingback: Flex Mobile Development: caching images using ContentCache | TechnoVeille

  13. Hi Mihai, this is a really good solution for any application that needs to reuse the same image list.

    Thanks a lot.

Leave a Reply

Your email address will not be published. Required fields are marked *