Caching bitmaps via LRU in Android Android 14.10.2017

Caching bitmaps via LRU in Android

The LruCache class is particularly well suited to the task of caching bitmaps, keeping recently referenced objects in a strong referenced LinkedHashMap and evicting the least recently used member before the cache exceeds its designated size.

The Android platform provides the LruCache class, as of API 12 (or in the support-v4 library). The LruCache class provides a least recently used cache (LRU cache) cache implementation. A LRU cache keeps track of the usage of its members. It has a given size and if this size is exceeded, it removes the items which have not be accessed the longest. This behavior is depicted in the following graphic.

android_lru_cache.webp

A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.

For determining the initial size of the cache, it is good practice to determine the size based on the total memory available on the device. For determining the available memory you can use the Runtime class. This is demonstrated by the following code.

public class BitmapCache extends LruCache<String, Bitmap> {
    public BitmapCache(int maxSize) {
        super(maxSize);
    }

    public static int getCacheSize() {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // use 1/8th of the available memory for this memory cache.
        return maxMemory / 8;
    }

    @Override
    protected int sizeOf( String key, Bitmap value ) {
        return value.getByteCount()/1024;
    }

    public Bitmap getBitmap(String key) {
        return this.get(key);
    }

    public void setBitmapOrDownload(final String key, String url, Context context, final ImageView iv) {
        if (hasBitmap(key)) {
            iv.setImageBitmap(getBitmap(key));
        } else {
            // get photo via Glide 4

            RequestOptions glideOptions = new RequestOptions();
            glideOptions.placeholder(R.drawable.logo);
            glideOptions.override(200, 100);

            BaseTarget bs = new BaseTarget<Bitmap>(){
                @Override
                public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
                    put(key, resource);
                    iv.setImageBitmap(resource);
                }

                @Override
                public void removeCallback(SizeReadyCallback cb) {}

                @Override
                public void getSize(SizeReadyCallback cb) {}
            };

            GlideApp.with(context.getApplicationContext()).asBitmap()
                    .load(url)
                    .apply(glideOptions)
                    .into(bs);
        }
    }

    public void setBitmap(String key, Bitmap bitmap) {
        if (!hasBitmap(key)) {
            this.put(key, bitmap);
        }
    }

    public boolean hasBitmap(String key) {
        return getBitmap(key) != null;
    }
}

Create singleton in MyApplication.

public class MyApplication extends Application {
    BitmapCache cache = null;

    @Override
    public void onCreate() {
        super.onCreate();
        cache = new BitmapCache(BitmapCache.getCacheSize());
    }

    public BitmapCache getCache() {
        return cache;
    }
}

Usage in MainActivity.java

Activity activity = MainActivity.this;
MyApplication app = (MyApplication) ((Activity) activity).getApplication();
BitmapCache cache = app.getCache();
ImageView iv = (ImageView) findViewById(R.id.ivPhoto);

String key = "android_rxjava.png";
String url = "http://en.proft.me/media/android/android_rxjava.png";

cache.setBitmapOrDownload(key, url, context, iv);

Usage in OtherActivity.java

Activity activity = OtherActivity.this;
MyApplication app = (MyApplication) ((Activity) activity).getApplication();
BitmapCache cache = app.getCache();
ImageView iv = (ImageView) findViewById(R.id.ivPhoto);

String key = "android_rxjava.png";
String url = "http://en.proft.me/media/android/android_rxjava.png";

if (cache.hasBitmap(key)) {
    iv.setImageBitmap(cache.getBitmap(key));
} else {
    cache.setBitmapOrDownload(key, url, context, iv);
}