Graeme
Graeme

Reputation: 25864

Caching Bitmaps - Checking current VM Budget

I'm wondering if anyone has any experience in an architecture which is designed to cache as many images as possible as discovered dynamically.

In my situation I request a Bitmap for each item clicked from a ListView. These Bitmaps are received via the network (not a cheap operation in terms of processing or network I/O). The user will likely load a few items from a list, switching between them. I cache these images so that subsequent requests of an item will results in an instant loading of the bitmap:

private Bitmap bitmap = null;
public Bitmap getBitmap(int id) {
    if(bitmap == null) bitmap = expensiveNetworkOperationToGetBitmap(id);
    return bitmap;
}

(please ignore thread blocking concerns)

However, it's perfectly possible the user will look through many items. Each one slowly taking up a slice of memory, eventually going over budget and crashing the app.

Which leads me to ask the question: Is it possible to dynamically detect when your caching is reaching a limit? If so it should be possible to start destroying the old images as you add new ones. See below for sample code:

private HashMap<Integer, Bitmap> bitmaps = new HashMap<Integer, Bitmap>();
public Bitmap getBitmap(int id) {
    Bitmap bitmap;
    if(!bitmaps.contains(id)) {
        if(// Bitmap budget close to being exeeded?) {
            bitmaps.keyValues().get(0).recycle();
            bitmaps.remove(bitmaps.keyValues().get(0));
        }
        bitmap = expensiveNetworkOperationToGetBitmap(id);
        bitmaps.put(id, bitmap);
    else {
        bitmap = bitmaps.get(id);
    }
    return bitmap;
}

Once more, please ignore concerns for the inefficency of popping the first Bitmap from the HashMap.

I'm hoping someone has a suggestion for the commented out section. If there is a better solution than this It'd be great to hear.

Upvotes: 1

Views: 409

Answers (2)

cistearns
cistearns

Reputation: 2508

You could use something like a collection of SoftReference to the bitmaps if you want to be able to have you cache grow basically unbounded to the limits of the VM Heap.

http://developer.android.com/reference/java/lang/ref/SoftReference.html

You will probably also want to implement in your activity onLowMemory() to also play nice with the system, given you want to have such a large cache.

Upvotes: 1

weakwire
weakwire

Reputation: 9300

You cannot detect then you reach the limit of the heap (you can't get the size of occupied heap at runtime AFAIK)

Here is what i do.

I have a listView and detect how many images are been displayed at any time (visible rows| columns). When a new bitmap is added i check into the adapter what cached images are not displayed by any imageview. If i find one or more i .recycle() them. When i need to reload the image (that i have stored to the phone's memory cache) i don't only check if that image exists on cache, i also check if the bitmap is recycled. If is recycled i reEncode it, else if none i download it again. That is the most safe operation. I also down scale the images depending on where are shown. For ex if i have a image 200x200 and the imageView is only 20x20 i downScale the image 10 times.

That way android will handle evey single image i display on it. It's all about recycling the bitmaps the save way android recycles the imageViews themselves.

You have to know how many bitmaps are currently visible so that you recycle the rest.

Also a handy trick is when you encode the image (you first have to download it to cache) do it with a fileDescriptor

BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
o2.inPurgeable = true;
o2.inTempStorage = new byte[16 * 1024];
o2.inDither = true;
o2.inInputShareable = true; 

FileInputStream fs = null;
try {
    fs = new FileInputStream(f);
    } catch (FileNotFoundException e) {
   e.printStackTrace();
}

if (fs != null) {
    Bitmap b = BitmapFactory.decodeFileDescriptor(fs.getFD(),null, o2);
  }

But if you do everything right you don't need the fileDescriptor(who is slower)

Upvotes: 1

Related Questions