Generic ImageLoader for Android

In this article I will talk about a tool I developed (and not only) for asynchronously loading images, caching and displaying them. At this stage of development, it can be used everywhere where you need to upload a picture to ImageView from the Internet or from the file system. All that is needed is the URL of the image (for the file system it will start with “file: //”) and the ImageView itself , into which the downloaded image will need to be put. In more detail about possibilities of the universal developed ImageLoader'but read below. It all started in one project, in which I happened to participate: it was necessary to implement viewing the news as a list. And then of course the question arose about displaying pictures in the list items. Pictures were downloaded from the Internet, so it was necessary to implement their asynchronous loading, display and caching. A quick search on the net led me to the next almost ready solution to this problem . The implemented LazyImageLoader asynchronously downloaded pictures from the Internet, cached them in the file system, and also stored them in memory. The storage method in memory was a simple HashMap without any weak links, as a result of which OutOfMemoryError began to fly out at a certain stage of scrolling the list (and there were many lists, too). HashMap was replaced with WeakValueHashMap , and then with its own implementation of Map with a memory limit. Gradually, on the basis of this LazyImageLoader, our own ImageLoader began to grow with its own chips and tricks. It could be used to display pictures not only in lists, but also in the gallery, and for simple “one-time” display. This ImageLoader was later reused in two other projects, which confirmed its viability. Having significantly refactored the existing code and put in acceptable beauty, I posted the sources on GitHub, where now the further optimization of the tool is being carried out, increasing flexibility and customizability. So, what can this ImageLoader do? Displaying images is clear. What about caching? Caching is divided into:
  • in-memory caching
  • file system caching (phone memory or SD card)
HashMap acts as a cache in memorywith "weak" links in the values. How “weak” (Soft, Weak, Phantom) is up to you:
public abstract class Cache {
	protected final Map> softMap = new HashMap>();
	public V get(K key) {
		if (softMap.containsKey(key)) {
			Reference reference = softMap.get(key);
			return reference.get();
		} else {
			return null;
		}
	}
	public void put(K key, V value) {
		softMap.put(key, createReference(value));
	}
	public void clear() {
		softMap.clear();
	}
	protected abstract Reference createReference(V value);
}
The current version uses a Bitmap cache that controls its size. This was implemented through the introduction of an additional “hard” list, where “strong” links to Bitmaps from softMaps were stored . As soon as the cache size exceeds the allowable limit, the "oldest" objects are removed from the "hard list", thereby losing a strong link. The weak link is still stored in softMap , but there Bitmap is already completely at the mercy of the Garbage Collector. When caching on the file system, the files are referred to as imageUrl.hashCode () and later on the same principle is used to search the cache. Consider the most fully functional ImageLoader's method is:
void displayImage(String imageUrl, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)
The parameters imageUrl and imageView , I think, will not raise questions. The DisplayImageOptions class is designed to configure the process of loading, caching and displaying a picture. Using it you can specify:
  • whether it is necessary to display a stub image in ImageView while a real picture is loading, and which stub to display;
  • whether to cache the downloaded image in memory;
  • Do I need to cache the downloaded image on the file system.
The ImageLoadingListener interface allows you to "listen" to the image loading process:
public interface ImageLoadingListener {
    void onLoadingStarted();
    void onLoadingComplete();
}
But if the current picture is in the cache in memory, then the listener will not throw events. Events are thrown on the UI stream, so you can touch the UI with a quiet soul in the listener . So, an example of using ImageLoader :
ImageLoader imageLoader = ImageLoader.getInstance(context);
DisplayImageOptions options = new DisplayImageOptions.Builder()
                                       .showStubImage(R.drawable.stub_image)
                                       .cacheInMemory()
                                       .cacheOnDisc()
                                       .build();
imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() {
    @Override
    public void onLoadingStarted() {
       spinner.show();
    }
    @Override
    public void onLoadingComplete() {
        spinner.hide();
    }
});
I won’t spread much about the mechanism of ImageLoader’s work. I will only say a couple of things:
  • tasks for displaying the picture are placed in the queue: if the picture is already in the cache on the file system, the task goes to the beginning of the queue, if not, to the end. Tasks are performed from the beginning of the queue, thereby displaying primarily cached pictures; ( UPD : After the introduction of a multi-threaded image display mechanism, this logic has been eliminated. Now two different pool of threads are engaged in loading cached and non-cached images: for cached ones, single-threaded, for others, multi-threaded)
  • not full-sized Bitmaps are stored in the cache in memory, but not less than the size needed for displaying in ImageView. This size is calculated based on the attributes maxWidth and maxHeight , layout_width and layout_height , the screen sizes of the device (the size of the original image is reduced by a power of two, in accordance with the recommendations for decoding images).
  • because First of all, ImageLoader is intended for displaying images in a list, and in lists, as a rule, reusing View is a good practice, then ImageLoader also monitors such situations by storing the loaded URL of the image in Tag ImageView with its own key.
Once again, I will give a link to the sources on GitHub. I hope this ImageLoader is useful to you.

UPD (12.19.2011): Some significant changes were made to the tool, details about them and the project as a whole can be read here .
UPD (02.23.2012): A lot of changes and improvements were made (including multithreading, external configuration). But the main API is basically the same. Now the tool is available as a jar. Versioning introduced.
UPD (03/11/2012): I wrote a detailed guide for using the library:

Also popular now: