ImageLoader源码分析(一)ImageLoader初始化



ImageLoader加载图片前要初始化,初始化时需要一个 ImageLoaderConfiguration

初始化代码:

ImageLoader.getInstance().init(configuration);

ImageLoaderConfiguration实例有两种默认创建方式:

1.  ImageLoaderConfiguration configuration1= ImageLoaderConfiguration.createDefault(context);

2.  ImageLoaderConfiguration configuration2=new ImageLoaderConfiguration.Builder(context).build();

在源码ImageLoaderConfiguration类中可以看到:

	public static ImageLoaderConfiguration createDefault(Context context) {
		return new Builder(context).build();
	}

所以上面两种初始化方式效果是一样的。

再来看一下Builder相关代码:

Builder构造函数:

		public Builder(Context context) {
			this.context = context.getApplicationContext();
		}

Builder中builder()方法相关代码:

		/** Builds configured {@link ImageLoaderConfiguration} object */
		public ImageLoaderConfiguration build() {
			initEmptyFieldsWithDefaultValues();
			return new ImageLoaderConfiguration(this);
		}

		private void initEmptyFieldsWithDefaultValues() {
			if (taskExecutor == null) {
				taskExecutor = DefaultConfigurationFactory
						.createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
			} else {
				customExecutor = true;
			}
			if (taskExecutorForCachedImages == null) {
				taskExecutorForCachedImages = DefaultConfigurationFactory
						.createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
			} else {
				customExecutorForCachedImages = true;
			}
			if (diskCache == null) {
				if (diskCacheFileNameGenerator == null) {
					diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
				}
				diskCache = DefaultConfigurationFactory
						.createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount);
			}
			if (memoryCache == null) {
				memoryCache = DefaultConfigurationFactory.createMemoryCache(context, memoryCacheSize);
			}
			if (denyCacheImageMultipleSizesInMemory) {
				memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator());
			}
			if (downloader == null) {
				downloader = DefaultConfigurationFactory.createImageDownloader(context);
			}
			if (decoder == null) {
				decoder = DefaultConfigurationFactory.createImageDecoder(writeLogs);
			}
			if (defaultDisplayImageOptions == null) {
				defaultDisplayImageOptions = DisplayImageOptions.createSimple();
			}
		}

我们看到有这么一句话:

if (memoryCache == null) {
				memoryCache = DefaultConfigurationFactory.createMemoryCache(context, memoryCacheSize);
			}

在DefaultConfigurationFactory.createMemoryCache中我们发现默认内存缓存使用的是自定义的LruMemoryCache(最近最少使用算法Least  Recently  Used),其源码如下

package com.nostra13.universalimageloader.cache.memory.impl;

import android.graphics.Bitmap;

import com.nostra13.universalimageloader.cache.memory.MemoryCache;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
 * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
 * become eligible for garbage collection.<br />
 * <br />
 * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.8.1
 */
public class LruMemoryCache implements MemoryCache {

	private final LinkedHashMap<String, Bitmap> map;

	private final int maxSize;
	/** Size of this cache in bytes */
	private int size;

	/** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
	public LruMemoryCache(int maxSize) {
		if (maxSize <= 0) {
			throw new IllegalArgumentException("maxSize <= 0");
		}
		this.maxSize = maxSize;
		this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
	}

	/**
	 * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
	 * of the queue. This returns null if a Bitmap is not cached.
	 */
	@Override
	public final Bitmap get(String key) {
		if (key == null) {
			throw new NullPointerException("key == null");
		}

		synchronized (this) {
			return map.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;
	}

	/**
	 * Remove the eldest entries until the total of remaining entries is at or below the requested size.
	 *
	 * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
	 */
	private void trimToSize(int maxSize) {
		while (true) {
			String key;
			Bitmap value;
			synchronized (this) {
				if (size < 0 || (map.isEmpty() && size != 0)) {
					throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
				}

				if (size <= maxSize || map.isEmpty()) {
					break;
				}

				Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
				if (toEvict == null) {
					break;
				}
				key = toEvict.getKey();
				value = toEvict.getValue();
				map.remove(key);
				size -= sizeOf(key, value);
			}
		}
	}

	/** Removes the entry for {@code key} if it exists. */
	@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);
			}
			return previous;
		}
	}

	@Override
	public Collection<String> keys() {
		synchronized (this) {
			return new HashSet<String>(map.keySet());
		}
	}

	@Override
	public void clear() {
		trimToSize(-1); // -1 will evict 0-sized elements
	}

	/**
	 * Returns the size {@code Bitmap} in bytes.
	 * <p/>
	 * An entry's size must not change while it is in the cache.
	 */
	private int sizeOf(String key, Bitmap value) {
		return value.getRowBytes() * value.getHeight();
	}

	@Override
	public synchronized final String toString() {
		return String.format("LruCache[maxSize=%d]", maxSize);
	}
}

我们发现使用的是java.util.LinkedHashMap<String, Bitmap>来存储Bitmap,并且是强引用,并没用采用软引用或弱引用。


再看这句话:

if (diskCache == null) {
				if (diskCacheFileNameGenerator == null) { //文件名生成器,源码中使用的是url的哈希值
					diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
				}
				diskCache = DefaultConfigurationFactory.createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount);
            }
				

当diskCacheSize>0或者diskCacheFileCount>0时使用的是自定义 的LruDiskCache,否则是UnlimitedDiskCache。


我们在builder中还看到这样一句代码:

if (downloader == null) {
				downloader = DefaultConfigurationFactory.createImageDownloader(context);
			}

在DefaultConfigurationFactory.createImageDownloader中我们看到使用的下载类是BaseImageDownloader,并重写了ImageDownloader接口中的getStream方法,BaseImageDownloader中我们看到了如下相关代码:

protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
		HttpURLConnection conn = createConnection(imageUri, extra);

		int redirectCount = 0;
                //请求重定向
                while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
			conn = createConnection(conn.getHeaderField("Location"), extra);
			redirectCount++;
		}

		InputStream imageStream;
		try {
			imageStream = conn.getInputStream();
		} catch (IOException e) {
			// Read all data to allow reuse connection (http://bit.ly/1ad35PY)
			IoUtils.readAndCloseStream(conn.getErrorStream());
			throw e;
		}
		if (!shouldBeProcessed(conn)) {
			IoUtils.closeSilently(imageStream);
			throw new IOException("Image request failed with response code " + conn.getResponseCode());
		}

		return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
	}
所以ImageLoader默认使用的网络请求是 HttpURLConnection,所以我们可以很方便的修改为自定义的网络请求,如使用okhttp等。


ImageLoader中默认采用BaseImageDecoder,可以矫正图像(旋转等)。



所以如果我们不想使用默认 的配置初始化,我们可以这样初始化:

		
		ImageLoaderConfiguration.Builder builder=new ImageLoaderConfiguration.Builder(context);
		
		builder.imageDownloader(new ImageDownloader() {
			
			/**
			 * @param imageUri  图片url 
			 * 
			 * @param  extra  可以忽略不使用
			 */
			@Override
			public InputStream getStream(String imageUri, Object extra) throws IOException {
				//使用第三方网络请求类库下载图片
				return null;
			}
		});
		//采用软引用
		builder.memoryCache(new  BaseMemoryCache() {
			@Override
			protected Reference<Bitmap> createReference(Bitmap value) {
				return new SoftReference<Bitmap>(value);
			}
		});
		
		builder.threadPoolSize(5);
		//省去部分自定义。。。。
		ImageLoader.getInstance().init(builder.build());
		
		

注意:builder.build(); 中已经判断是否为null了,所以默认值不会覆盖用户自定义的配置




你可能感兴趣的:(android,源码分析,ImageLoader)