1

I am working on an application that makes use of a dynamically populated list view of which each item contains an image that needs to be downloaded. Additionally, when clicking on any item in the list, it opens a detail page which once again shows the same image in addition to a few more.

As we know, it is best to use an async task to do the actual download and good practice to use a caching mechanism to both speed up image display as well as minimize actual data usage in multiple downloads.

After searching around for quite a bit, I ended up using the Google example code located at http://developer.android.com/resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html which claims to be both an async and caching method to download images.

The problem I am having, when called, this class does not appear to be caching the images correctly. So here goes ....

The portion of code in my custom class (that builds the list view) where I call the image download is this:

ImageDownloader getimage = new ImageDownloader();
getimage.download(logoURL, ivLogo);

With ImageDownloader being the class referenced above.

Images load, but it is apparent they are loading from the web each time. I added some logging into ImageDownloader.class to help track what is happening when. What I found, the first time the image is requested, it fails the first call to get from cache (as expected), getBitmapFromCache returns null and forceDownload is called.

public void download(String url, ImageView imageView, String cookie) {
    resetPurgeTimer();
    Bitmap bitmap = getBitmapFromCache(url);
    Log.i(LOG_TAG, "Bitmap is: " + bitmap);

    if (bitmap == null) {
        Log.i(LOG_TAG, "Forcing Download");
        forceDownload(url, imageView, cookie);
    } else {
        cancelPotentialDownload(url, imageView);
        imageView.setImageBitmap(bitmap);
    }
}

As execution progresses down the class to the point where it adds the image/url to the cache

private void addBitmapToCache(String url, Bitmap bitmap) {
    Log.i(LOG_TAG, "Incoming Add Cache Info: URL: " + url + " Bitmap: " + bitmap);
    if (bitmap != null) {
        Log.i(LOG_TAG, "Entering the add image to cache section");
        synchronized (sHardBitmapCache) {
            sHardBitmapCache.put(url, bitmap);
            Bitmap returnedbitmap = getBitmapFromCache(url);
            Log.i(LOG_TAG, "Returned bitmap immediately after adding: " + bitmap);
        }
    }
}

I added logging to both verify the cache was added, and then performed a getBitmapFromCache, and the logs show the returned bitmap/url hash returns properly.

Incoming request to get Image, URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png

Trying Hard Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png

Hard Bitmap is: null

Trying Soft Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png

Return from initial entry call: null

Entering forced Download

Incoming Add Cache Info: URL:http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.pngBitmap: android.graphics.Bitmap@40762f28

Entering the add image to cache section

Trying Hard Cache withURL: http://www.yourlogoresources.com/wpcontent/uploads/2011/11/Wendys-logo.png

Hard Bitmap is: android.graphics.Bitmap@40762f28

Returned bitmap immediately after adding:android.graphics.Bitmap@40762f28

So it appears the cache is being populated properly, however, the next time that image is requested (ie when you scroll the listview), the cached image is not found again with getBitmapFromCache(url) returning null from the entry point download.

Incoming request to get Image, URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png

Trying Hard Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png

Hard Bitmap is: null

Trying Soft Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png

Return from initial entry call: null

Entering forced Download

So I am at a loss here as to why it appears the cache is populated, but when it is checked again, nothing but null is returned.

EDIT

I ended up using a static reference to the ImageDownloader from my main Activity as follows:

public class myActivity extends Activity implements OnClickListener {
     public final static ImageDownloader GetImage = new ImageDownloader();

Then called that like this anywhere I needed an ImageDownload:

    myActivity.GetImage.download(logoURL,  ivLogo);

Thanks for all the help folks.

brad
  • 25
  • 4

1 Answers1

1

Look into the code of ImageDownloader.

It is using a primary cache private final HashMap<String, Bitmap> sHardBitmapCache

and a secondary static cache private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache

Entries kicked out of the primary cache are put into the secondary cache.

Now everytime you want to download an Image, if you create a new instance of ImageDownloader, you are effectively not using the caching mechanism because each ImageDownloader instance will only have that single image you downloaded using this instance.

PS: Your code in the question looked like you are using an instance per download.

Vikram Bodicherla
  • 7,133
  • 4
  • 28
  • 34
  • Vikram, yes I believe you are correct, I stand up a new instance for every image. I presume I should find a way to only stand up a single ImageDownloader instance and simply use that single instance for each download? – brad Jan 27 '12 at 14:14
  • And to clarify, I have an Activity class that makes the initial call to a CustomAdapter class (with an array as input), which in turn calls the ImageDownloader class for each entry in the array. Being really new to Java, I am just not sure how I would go about instantiating only a single instance of ImageDownloader, but being able to access it from multiple other classes. Any pointers would be appreciated. – brad Jan 27 '12 at 15:09
  • You could expose an instance of the ImageDownloader via the Application object. Here's an example: http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables. Or you could simply make it a static object – Vikram Bodicherla Jan 27 '12 at 15:26
  • Thanks again Vikram, I will check it out. – brad Jan 27 '12 at 16:29
  • I ended up implementing a static object in my main Activity that instantiates ImageDownloader, then use that object to reference all future downloads. – brad Jan 27 '12 at 19:44
  • Keep an eye out for memory leaks, you are talking images and static. – Vikram Bodicherla Jan 30 '12 at 07:27