Android: Image Download and Caching

In one of my projects I used a ListView which displayed images from the web and I needed a solution that cached the downloaded images on a device’s external storage. I managed to find a class on the Android website which does exactly that. I modified it slightly to fit my needs(My custom image caching procedures and Android 2.1 compatibility). It has the following features:

  • It downloads the image and caches it on the external storage.
  • If the external storage device is full, only the image will be returned. No caching will take place.
  • If no external storage device is found, it will attempt to cache to the internal storage device.
  • It supports multiple downloads at once for  multiple ImageViews.
  • It keeps track of the download order of the images for ImageViews. (Perfect for ListViews)
  • It is also thread safe

Usage:

Add the WRITE_EXTERNAL_STORAGE permission to your application manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Then initialize the object and use it:

ImageView imgAvatar = (ImageView) convertView.findViewById(R.id.imgAvatar);

ImageDownloader imageManager = new ImageDownloader;
imageManager.download(expert.ThumbnailFileName, imgAvatar);

The best way to use the ImageDownloader is to create a single instance for your entire application or activity. The images is saved by calculating a hash from the url and then saving the image with the hash as its filename. So when you try to download an image, and the hash already exists, it means that the image is already downloaded and the image is returned from the cache.

Take note that this class was created to compile and run under Android 2.1. It works perfect with the latest Android devices and should be able to compile with the newer Android Platforms.

Update – 26 November 2012

ImageDownloader 1.1

  • Fixed out of memory bug that occurs when a lot of images are loaded at once

Older Versions

Here is the download link: ImageDownloader.java

Update – 16 June 2012

If you used the previous version of the ImageDownloader, you might have noticed a slight lag while scrolling through a listview. To improve the lagging, I have added in-memory caching of the images.

Here it is: ImageDownloader v1.01

Full Workspace

Ok, I have had a couple of mails and requests about a workspace which implements the image downloader. A proper working example.

Here it is: ImageDownloader Test Workspace

VN:F [1.9.20_1166]
Rating: 4.4/5 (11 votes cast)
Android: Image Download and Caching, 4.4 out of 5 based on 11 ratings

Add a comment »16 comments to this article

  1. what if my image at the url changes ??

    Reply

    • At the moment, it will not re-fetch the image if it was updated on the server. For that to happen, the image must be wiped from the cache directory on the external / internal storage. I’ll work something into my next update ;)

      Reply

  2. works perfectly !! thanks a lot !! :)

    Reply

  3. Hi, is there any link that can let me download the whole workspace? I need it urgently for my project. Hope you can provide me the code so that I can refer. Thank you very much and your help is very much appreciated.

    Reply

  4. Thank you very much , great work I uswd it on my apps
    but how can I only download images that are focused right now ?

    Reply

    • Hi Muhannad,
      That is the nice thing about combining the ImageDownloader with a ListView. It will only download images you specifically request. It as per the workspace example, the ImageDownloader is told to only download an image if its designated row is being displayed. If you scroll away, it will still continue to download the image and cache it.

      Please note that the images that are used in the example are quite large, so they might take a while to appear. I did it deliberately to demonstrate the caching effect. It should be a lot faster if you use 100×100 images/thumbnails.

      Reply

  5. Really Good tutorial..Easy to implement. Thanks a lot!

    Reply

  6. Many thanks, this did exactly what I was looking for. I’d like to note that this used in combination with a custom adapter makes for an amazingly easy lazy loading solution. Cheers!

    Reply

  7. Hi,

    I was redirected here from a StackOverflow thread.
    I had a quick read on your class and just feel like giving a feedback. I know my words below are harsh but they’re meant to be a positive feedback to let you improve.

    That class might work but it’s wrong in several different levels.
    1. inheritance/classes/separation: it mixes UI handling with network calls with disk IO with some attempt of memory management. Write smaller more manageable and easier to test classes that does just ONE thing.

    2. file caching on Android should be com /Android/data/<package.name>/ because this folder will be automatically deleted whenever the app is uninstalled and not filling the memory with garbage.

    3. on Android image caching folder should have a .noMedia file to avoid the media scanner to go through it.

    4. Maps and WeakReferences are not an acceptable RAM cache on Android for bitmaps because of the memory available for the VM. It’s necessary something a bit more robust. The Android team suggests the LruCache class located on the Android compatibility package (and it is a great solution).

    5. write, read, encode and decode of the bitmap files are being called on the UI thread. Any IO operation (even faster ones like disk) should never eve be called on the UI thread.

    6. Bitmaps that were already downloaded but cancelled are not being written to the disk. Why not? We know that the chances are that this is being used on a list view and the user is likely to scroll back to it.

    7. There’s not maximum limit on the amount of data you’ll be writing on the disk. A cache should never just grown indefinitely.

    On the oficial android developers page you can find a lot of good information regarding those things I pointed out.
    http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

    Reply

    • Hi Ronaldo :)

      Thank you for your constructive feedback. It is well appreciated. I do have reasons for all of the points you listed above, and I more than likely won’t change a lot regarding it.

      1. The whole idea of this class is to have one class. To have everything in one place. I do agree that it needs some restructuring to be more logical though.

      2. The directory to which the cached data is written, is left entirely up to the developer. You as a developer can decide whether or not the cache should be removed. (I recently had a project where the cache was to be kept as per requirement. Due to the nature of the images)

      3. Again, this is the developer’s decision. But I’ll make improvements to make it easier to turn this functionality on.

      4. Memory Management is currently a big issue for this class. Even so, I have received a lot of positive feedback for creators of actual applications which had no issues at all. I also received some not so positive feedback from stress testers which loaded a a lot of images at once (+4000 images). This at the moment, is my main priority.

      5. Once again, this I left up to the developer. Feel free to chuck the download function into a thread.

      6. If the images finished downloading, they will be saved to the cache. At the moment though, a download is canceled when a imageview is attempting to load another image. This one could go either way. At the moment it is to prevent a lot of images to be downloaded at once, for example, when a user scrolls quickly through a list.

      7. Of course not, but I still want to leave the option open to the developer. I see if I can push something in to setup a limit if it is needed.

      Again, thank you for your feedback. It gave me a lot of ideas for other improvements as well.

      Kind Regards,
      Admin

      Reply

  8. Hi and thanks for sharing your code.
    I tried to use the last version of your class (26 November 2012) but I noticed that images already downloaded are getting redownloaded after the relative ImageView gets out of focus and on focus again.
    In particular the bitmap instance inside the WeakReference is null in those cases.. Am I missing something?
    Thanks in advance

    Reply

    • Thanks for that catch. I’ll have an update ready for that soon as possible :)

      Reply

  9. Good job..thank u

    Reply

  10. Hey There. I found your blog using msn. This is an extremely well written article. I will make sure to bookmark it and return to read more of your useful information. Thanks for the post. I will certainly comeback.

    Reply

  11. Hey Thnx for the code it works like a charme :D .

    I have a question how can u add an icon holder instead of black background while downloading?

    Reply

    • As soon as you have a reference to the ImageView, you just load a default image while the downloader is running the background :)

      Reply


8 × = seventy two

Copyright © All Rights Reserved · Green Hope Theme by Sivan & schiy · Proudly powered by