Sunday, April 21, 2013

Image Caching with LruCache

Caching images

When we are building an application, whether it's a web app or a mobile app or whatsoever, we want to make sure it runs as fast as possible. One of the most important things we have to keep in mind is caching. If we are using certain resources in our application, we should know that it isn't always that easy to retrieve them. So we should always avoid to retrieve the same resources multiple times. This can be done by caching them. In this tutorial I'm going to give an example of how to cache images in your Android app.

Images can easily get quite large, so retrieving them can take up quite some time. Since mobile devices have limited resources (such as battery life, data transfers, etc), it's twice as important to make sure we cache images the right way.

SoftReferences?

Java already provides an easy way to do this. It's called SoftReferences. You can read all about it here. The only problem is that there is an issue in Android that causes the garbage collector not to respect these SoftReferences. You can read all about this issue here. The Android System provides another way of caching your objects, called LruCache.

LruCache

LRU stands for Least Recently Used. This already tells us a lot about how this cache would work. LruCaches have a fixed cache size and work like a queue, you might say.  If a new object is added to the cache, it's placed in the beginning of the queue. If the app asks to get an object from the cache, it's also placed in the beginning of this queue. This way the least recently used objects are left at the beginning of the queue.



If the cache doesn't have enough memory left, it deletes the least recently used objects until it has enough free space to store the new object(s).


API levels

LruCache was only added to API level 12 but don't worry, they have included this in the support library v4.

Code it!

Okay, it's nice to know how it works but now we still have to implement it in our code. For this tutorial I've made a small app that puts all the thumbnails of the images on the device in a large list. If the user scolls down, the images are loaded. This is actually really simple to implement. First let's have a look on how we could code the LruCache.


public class ThumbnailCache extends LruCache<Long, Bitmap>{



  public ThumbnailCache(int maxSizeInBytes) {

   super(maxSizeInBytes);

  }



  @Override

  protected int sizeOf(Long key, Bitmap value) {

   return value.getByteCount();

   

   //before API 11

   //return value.getRowBytes() * value.getHeight();

  }

  

  

 }

We have to add a value in the constructor of this LruCache. This is the maximum size of the cache in bytes. It can be hard to know how much this should be. You can get some help of the ActivityManager to do this.
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
If we run this code, we'll get the memory limit of our application. This depends on what device you are using (on a Nexus S and Galaxy SII it's 48MB). Have a look here for more information.
It's obvious that we don't want our image cache to take up all of the memory of my application. So we only take a part of this. For this example let's say we use a sixth.
mCache = new ThumbnailCache(memoryClassBytes / 6);
Now place the image in our LruCache when it is retrieved using this line of code.
mCache.put(photoId, bitmap);

And check if it is in cache before retrieving it like this:
Bitmap cachedResult = mCache.get(photoId);

             if (cachedResult != null) {

                 imageView.setImageBitmap(cachedResult);

             }else{
              //retrieves the thumbnail and puts it in an imageview
              new ThumbnailTask(imageView).execute(photoId);

That's all you need to do. Give it a try!