Use LRU Image Caching In Conjuction With HTTPResponseCache for Disk and Memory Caching

1.1k Views Asked by At

Initially the objective was to use both disk and memory cache. This would require implementing LRU cache and DiskLruCache. However, since HTTPResponse cache uses disk space, I chose to use LRU cache and do con.setUseCaches(true);

The problem is that I don't really understand what gets implemented first. For LRU and DiskLru cache, this is the algorithm:

enter image description here

i.e.

first check memory cache for an image

if there is an image, return it and update caches

else check disk cache

if disk cache has an image, return it and update caches

else download image from the internet, return it and update caches

Now with the code below:

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    private int inSampleSize = 0;

    private String imageUrl;

    private BaseAdapter adapter;

    private ImagesCache cache;

    private int desiredWidth, desiredHeight;

    private Bitmap image = null;

    private ImageView ivImageView;

    Context mContext;

    public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight) {
        this.adapter = adapter;

        this.cache = ImagesCache.getInstance();

        this.desiredWidth = desiredWidth;

        this.desiredHeight = desiredHeight;
    }

    public DownloadImageTask(Context mContext, ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight) {
        this.cache = cache;

        this.ivImageView = ivImageView;

        this.desiredHeight = desireHeight;

        this.desiredWidth = desireWidth;

        this.mContext = mContext;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        imageUrl = params[0];

        return getImage(imageUrl);
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        super.onPostExecute(result);
        if (result != null) {
            cache.addImageToWarehouse(imageUrl, result);
            if (ivImageView != null) {
                ivImageView.setImageBitmap(result);
            }
            else {
            }
            if (adapter != null) {
                adapter.notifyDataSetChanged();
            }
        }
                /*
        * IMPORTANT:
        * This enables your retrieval from the cache when working offline
        */
        /***
         * Force buffered operations to the filesystem. This ensures that responses
         * written to the cache will be available the next time the cache is opened,
         * even if this process is killed.
         */
        HttpResponseCache cache = HttpResponseCache.getInstalled();
        if(cache != null) {
            //the number of HTTP requests issued since this cache was created.
            Log.e("total num HTTP requests", String.valueOf(cache.getHitCount()));
            //the number of those requests that required network use.
            Log.e("num req network", String.valueOf(cache.getNetworkCount()));
            //the number of those requests whose responses were served by the cache.
            Log.e("num use cache", String.valueOf(cache.getHitCount()));
            //   If cache is present, flush it to the filesystem.
            //   Will be used when activity starts again.
            cache.flush();
            /***
             * Uninstalls the cache and releases any active resources. Stored contents
             * will remain on the filesystem.
             */
            //UNCOMMENTING THIS PRODUCES A java.lang.IllegalStateException: cache is closedtry {
              //  cache.close();
            //}
            //catch(IOException e){
              //  e.printStackTrace();
            //}
        }
    }

    private Bitmap getImage(String imageUrl) {
        if (cache.getImageFromWarehouse(imageUrl) == null) {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            options.inSampleSize = inSampleSize;
            //installing the cache at application startup.
            try {
                HttpResponseCache cache = HttpResponseCache.getInstalled();
                if (cache == null) {
                    File httpCacheDir = new File(mContext.getCacheDir(), "http");
                    long HTTP_CACHE_SIZE_IN_BYTES = 1024 * 1024 * 1024; // 1GB
                    HttpResponseCache.install(httpCacheDir, HTTP_CACHE_SIZE_IN_BYTES);
                    //Log.e("Max DiskLRUCache Size", String.valueOf(DiskLruCache.getMaxSize());
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            try {
                int readTimeout = 300000;
                int connectTimeout = 300000;
                URL url = new URL(imageUrl);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setUseCaches(true);
                connection.addRequestProperty("Cache-Control", "max-stale=" + maxStale);
                connection.setConnectTimeout(connectTimeout);
                connection.setReadTimeout(readTimeout);
                InputStream stream = connection.getInputStream();
                image = BitmapFactory.decodeStream(stream, null, options);
                int imageWidth = options.outWidth;
                int imageHeight = options.outHeight;
                if (imageWidth > desiredWidth || imageHeight > desiredHeight) {
                    System.out.println("imageWidth:" + imageWidth + ", imageHeight:" + imageHeight);
                    inSampleSize = inSampleSize + 2;
                    getImage(imageUrl);
                }
                else {
                    options.inJustDecodeBounds = false;
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setUseCaches(true);
                    connection.addRequestProperty("Cache-Control", "only-if-cached");
                    connection.setConnectTimeout(connectTimeout);
                    connection.setReadTimeout(readTimeout);
                    stream = connection.getInputStream();
                    image = BitmapFactory.decodeStream(stream, null, options);
                    return image;
                }
            }
            catch (Exception e) {
                Log.e("getImage", e.toString());
            }
        }
        return image;
    }
}

It appears as though in doInBackground() I am saving to HttpResponseCache and in onPostExecute() I am saving the same image to LRUCache. What I would like to do is:

If the image is not cached, save it to HttpResponseCache If HttpResponseCache is full, save to LRUCache. If both are full, use the default delete mechanisms to remove old unused images. The problem is saving to both caches and not either

EDIT REPHRASE QUESTION

**

Since both are caching the same image, twice and saving cache to the same filesystem, perhaps the question should be which one to use as there seems to be no point in using both...

**

1

There are 1 best solutions below

0
On BEST ANSWER

If anyone wants to re-use any of the above code, I would take only the http response cache and not use the LRU cache since especially if you are caching webservice response e.g. JSON,xml. Why?

I once lost 200MB of device storage due to the LRU cache implementation above.

Advantages of HTTPResponseCache:

  • Caches HTTP and HTTPS responses to the filesystem so they may be reused, saving time and bandwidth
  • HttpUrlConnection does: Automatic handling of the caching mechanisms,
  • Speeds up response time of the application with the help of HttpResponseCache
  • It has been available since API level 1=> it has stood the test of time and is robust

On the other hand:

While LRUCache has its advantages over DiskLRUCache:

  • You have to implement the class (and other helper classes), meaning if the code on android developers changes, you will have to constantly download and edit your local version after your app breaks after the previous implementation would have been deprecated.
  • After removing the image, you might still find your disk space being used as the image will be somewhere in your device (as with my case).

That is the conclusion...