In this article I will talk about the tool I developed (and not only) for asynchronous loading of images, their caching and display. At this stage of development, it can be used everywhere, where you need to upload a picture to the 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 begin with “file: //”) and the
ImageView itself , in which the downloaded image will need to be put. For more information about the capabilities of the universal developed
ImageLoader, see below.
It all started in one project, in which I had the opportunity to participate: it was necessary to realize the viewing of news by a list. And then of course the question arose of displaying pictures in list items. Pictures were loaded from the Internet, so it was necessary to implement their asynchronous loading, mapping and caching. A quick search on the web brought me to the
next almost complete solution to this problem . The implemented
LazyImageLoader asynchronously downloaded images from the Internet, cached them in the file system, and also stored them in memory. The method of storing in memory was a simple HashMap without any weak references, as a result of which
OutOfMemoryError began to fly out at a certain stage of scrolling the list (and there were many lists). HashMap was replaced with
WeakValueHashMap , and then with its own Map implementation with a restriction on memory usage. Gradually, on the basis of this LazyImageLoader, its own
ImageLoader began to grow with its own tricks and frills. It could be used to display images not only in lists, but also in the gallery, and for a simple "one-time" display. This ImageLoader was later reused in two other projects, which confirmed its viability. Significantly refactoring the existing code and bringing in an acceptable beauty, I put the
source code on GitHub , which is now gradually further optimizing the tools, increasing flexibility and customization. So what can this ImageLoader still do? Display images is clear. What about caching?
Caching is divided into:
- memory caching
- file system caching (phone memory or SD card)
HashMap <String, Bitmap> acts in the role of a cache in memory, with “weak” references in values. How "weak" (Soft, Weak, Phantom) is up to you:
public abstract class Cache<K, V> { protected final Map<K, Reference<V>> softMap = new HashMap<K, Reference<V>>(); public V get(K key) { if (softMap.containsKey(key)) { Reference<V> 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<V> 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” references to the Bitmap from
softMap were stored . As soon as the cache size exceeds the allowed 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 Garbage Collector.
When caching on the file system, the files are referred to as
imageUrl.hashCode () and later on the same principle will be searched in the cache. Consider the most complete ImageLoader's method is:
void displayImage(String imageUrl, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)
The
imageUrl and
imageView parameters, I think, will not cause questions. The
DisplayImageOptions class is designed to customize the process of loading, caching and displaying images. With it you can specify:
- whether it is necessary to display a blank image in ImageView, while the real image is being loaded, and which particular blank image to display;
- whether to cache the downloaded image in memory;
- whether 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 cached in memory, the
listener will not throw events. Events are thrown on a UI thread, so you can touch the UI in a
listener with peace of mind. 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 will not spread strongly about the mechanism of operation of ImageLoader. I will say only a couple of things:
- tasks for displaying pictures are put in the queue: if the picture is already in the cache on the file system, the job gets to the beginning of the queue, if not - to the end. Tasks are performed from the beginning of the queue, thereby displaying the cached pictures first; ( UPD : After the introduction of a multi-threaded mechanism for displaying images, this logic was abolished. Now, two different pools of threads are loading cached and uncached images: single-threaded for cached ones, and multi-threaded for the others)
- not full-size Bitmaps are stored in the cache, but the size is not less than that needed to be displayed in the ImageView. This size is calculated based on the attributes maxWidth and maxHeight , layout_width and layout_height , the size of the screen 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 designed to display images in the list, and in lists, as a rule, it is a good idea to reuse the View, and ImageLoader tracks such situations, saving the downloadable URL of the image in Tag ImageView with its own key.
Once again I will give the link to
source codes on GitHub. I hope this
ImageLoader is useful also to you.
UPD (12/19/2011): Some significant changes were made to the tool, you can read more about them and the project as a whole
here .
UPD (02/23/2012): A lot of changes and improvements have been made (including multithreading, external configuration). But the basic API is basically the same. Now the tool is available as a jar. Versioning introduced.
UPD (11.03.2012): Wrote a detailed guide on the use of the library: