Image caching was a significant part of an app which I've built and put into the app store. The app needs to download images and cache them both in memory and on the SDCard so that their scope extends beyond a single run.
The general idea was to add the images to a Caching Manager which both a) stores the image in an associative container (HashMap), via a key based on the meta-data, and b) writes the image file to the SDCard.
During low memory conditions, I release the HashMap. Yet images can still be retrieved from the SD_Card and once again cache into memory.
I was able to do this without recycling and still see no memory issues. As I understand it, recycling is not necessary but helps to get an earlier release of memory used for "Bitmaps" due to the fact that allocation for Bitmaps, in pre-Gingerbread OSes use Native Memory. i.e. Memory that's not part of the Dalvik heap. So the Garbage Collector doesn't free this memory, rather it's freed by implementation specific policies.
This is from the Cache_Manager class:
public static synchronized void addImage(Bitmap b, String urlString, boolean bSaveToFile, IMAGE_TYPES eIT, boolean bForce)
{
String szKey = getKeyFromUrlString(urlString, eIT);
if (false == m_hmCachedImages.containsKey(szKey) || bForce)
{
m_hmCachedImages.put(szKey, b);
if (bSaveToFile)
{
boolean bIsNull = false;
// Write a null object to disk to prevent future query for non-existent image.
if (null == b)
{
try
{
bIsNull = true;
b = getNullArt();
}
catch (NullPointerException e)
{
e.printStackTrace();
throw e;
}
}
// Don't force null art to disk
if (false == File_Manager.imageExists(szKey) || (bForce && bIsNull == false))
File_Manager.writeImage(b, szKey);
}
}
}
// Here an example of writeImage() from the File_Manager class
public static void writeImage(Bitmap bmp, String szFileName)
{
checkStorage();
if (false == mExternalStorageWriteable)
{
Log.e("FileMan", "No Writable External Device Available");
return;
}
try
{
// Create dirctory if doesn't exist
String szFilePath = getFilesPath();
boolean exists = (new File(szFilePath)).exists();
if (!exists)
{
new File(szFilePath).mkdirs();
}
// Create file
File file = new File(szFilePath, szFileName);
// Write to file
FileOutputStream os = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, os);
} catch (IOException e)
{
// Unable to create file, likely because
// external storage is
// not currently mounted.
Log.e("FileMan", "Error writing file", e);
} catch (Exception e)
{
e.printStackTrace();
throw e;
}
}