A list,

UniversalImageLoader is an open source image loading library, which uses memory cache, local cache and network download to load bitmap. Memory caches have cache strategies such as LruMemoryCache(the default).

2. Basic Usage

// 1. Set the options for loading images
DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
        .cacheInMemory(true)
        .cacheOnDisk(false)
        .bitmapConfig(Bitmap.Config.ARGB_8888)
        .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
        .considerExifParams(true)
        .build();
// 2. Load the image
ImageLoader.getInstance().loadImage(netUrl, displayImageOptions,
        new ImageLoadingListener() {
        @Override
        public void onLoadingStarted(String imageUri, View view) {}@Override
		public void onLoadingFailed(String imageUri, View view, FailReason failReason) {}@Override
		public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {}@Override
		public void onLoadingCancelled(String imageUri, View view) {}});Copy the code

Third, source code analysis

Imageloader.getinstance ().loadimage () imageloader.getInstance ().loadimage ()

public class ImageLoader {
	// Call entry
	public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        checkConfiguration();
        if (targetImageSize == null) {
            targetImageSize = configuration.getMaxImageSize();
        }
        if (options == null) {
            options = configuration.defaultDisplayImageOptions;
        }

        NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
        displayImage(uri, imageAware, options, listener, progressListener);
    }
    
    // Load the image
    public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        1. Check whether the memory cache exists. The default value is LruMemoryCache
        Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
        if(bmp ! =null && !bmp.isRecycled()) {
            / /... Omit some code
            listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
        } else {
            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            // Encapsulates LoadAndDisplayImageTask to find/send network requests from the local cache
            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                    defineHandler(options));
            if (options.isSyncLoading()) {
                displayTask.run();
            } else{ engine.submit(displayTask); }}}}Copy the code

4. Memory cache

The memory cache LruMemoryCache implements the least-recently used algorithm via LinkedHashMap.

public class LruMemoryCache implements MemoryCache {

    private final LinkedHashMap<String, Bitmap> map;
    
    public LruMemoryCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0.0.75 f.true);
    }
    @Override
    public final Bitmap get(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        synchronized(this) {
            returnmap.get(key); }}/**
     * Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue.
     */
    @Override
    public final boolean put(String key, Bitmap value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        synchronized(this) {
            size += sizeOf(key, value);
            Bitmap previous = map.put(key, value);
            if(previous ! =null) {
                size -= sizeOf(key, previous);
            }
        }

        trimToSize(maxSize);
        return true;
    }
    @Override
    public final Bitmap remove(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        synchronized(this) {
            Bitmap previous = map.remove(key);
            if(previous ! =null) {
                size -= sizeOf(key, previous);
            }
            returnprevious; }}}Copy the code

5. LinkedHashMap implements LRU

LRU is the least recently used algorithm. There are many algorithms implemented, such as adding a timestamp field to each data. Considering the efficiency of algorithm execution, linked list +HashMap is generally adopted. The internal implementation of LinkedHashMap LRU is implemented using HashMap+ double linked list. When a new item needs to be inserted, if the new item exists in the list (generally called hit), the node is moved to the head of the list. If it does not exist, a new node is created and placed in the head of the list. When accessing data, if the item exists in the list, move the node to the head of the list, otherwise -1 is returned. In this way, the head node stores the recently accessed data items, and the tail node stores the oldest unaccessed data items. If the cache is full, the tail node can be deleted.

public class LinkedHashMap<K.V> extends HashMap<K.V> implements Map<K.V> {
	// Header node, the most recently accessed data
	transient LinkedHashMapEntry<K,V> head;
	// Tail node, the longest unaccessed data
    transient LinkedHashMapEntry<K,V> tail;
    
    // Get a node
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
        	// Update the header node after the node is accessed
            afterNodeAccess(e);
        return e.value;
    }
    
    // Update the header node after the node is accessed
    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMapEntry<K,V> last;
        if(accessOrder && (last = tail) ! = e) { LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after; p.after =null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if(a ! =null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else{ p.before = last; last.after = p; } tail = p; ++modCount; }}}Copy the code