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:
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
**
**
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:
On the other hand:
While LRUCache has its advantages over DiskLRUCache:
That is the conclusion...