Android-Universal-Image-Loader 图片框架有三个常用的组件。
DisplayImageOptions、ImageLoader、ImageLoaderConfiguration。
一.源码
DisplayImageOptions源码
public final class DisplayImageOptions {
private final int stubImage;
private final int imageForEmptyUri;
private final int imageOnFail;
private final boolean resetViewBeforeLoading;
private final boolean cacheInMemory;
private final boolean cacheOnDisc;
private final ImageScaleType imageScaleType;
private final Options decodingOptions;
private final int delayBeforeLoading;
private final Object extraForDownloader;
private final BitmapProcessor preProcessor;
private final BitmapProcessor postProcessor;
private final BitmapDisplayer displayer;
private final Handler handler;
private DisplayImageOptions(DisplayImageOptions.Builder builder) {
this.stubImage = builder.stubImage;
this.imageForEmptyUri = builder.imageForEmptyUri;
this.imageOnFail = builder.imageOnFail;
this.resetViewBeforeLoading = builder.resetViewBeforeLoading;
this.cacheInMemory = builder.cacheInMemory;
this.cacheOnDisc = builder.cacheOnDisc;
this.imageScaleType = builder.imageScaleType;
this.decodingOptions = builder.decodingOptions;
this.delayBeforeLoading = builder.delayBeforeLoading;
this.extraForDownloader = builder.extraForDownloader;
this.preProcessor = builder.preProcessor;
this.postProcessor = builder.postProcessor;
this.displayer = builder.displayer;
this.handler = builder.handler;
}
public boolean shouldShowStubImage() {
return this.stubImage != 0;
}
public boolean shouldShowImageForEmptyUri() {
return this.imageForEmptyUri != 0;
}
public boolean shouldShowImageOnFail() {
return this.imageOnFail != 0;
}
public boolean shouldPreProcess() {
return this.preProcessor != null;
}
public boolean shouldPostProcess() {
return this.postProcessor != null;
}
public boolean shouldDelayBeforeLoading() {
return this.delayBeforeLoading > 0;
}
public int getStubImage() {
return this.stubImage;
}
public int getImageForEmptyUri() {
return this.imageForEmptyUri;
}
public int getImageOnFail() {
return this.imageOnFail;
}
public boolean isResetViewBeforeLoading() {
return this.resetViewBeforeLoading;
}
public boolean isCacheInMemory() {
return this.cacheInMemory;
}
public boolean isCacheOnDisc() {
return this.cacheOnDisc;
}
public ImageScaleType getImageScaleType() {
return this.imageScaleType;
}
public Options getDecodingOptions() {
return this.decodingOptions;
}
public int getDelayBeforeLoading() {
return this.delayBeforeLoading;
}
public Object getExtraForDownloader() {
return this.extraForDownloader;
}
public BitmapProcessor getPreProcessor() {
return this.preProcessor;
}
public BitmapProcessor getPostProcessor() {
return this.postProcessor;
}
public BitmapDisplayer getDisplayer() {
return this.displayer;
}
public Handler getHandler() {
return this.handler == null ? new Handler() : this.handler;
}
public static DisplayImageOptions createSimple() {
return (new DisplayImageOptions.Builder()).build();
}
public static class Builder {
private int stubImage = 0;
private int imageForEmptyUri = 0;
private int imageOnFail = 0;
private boolean resetViewBeforeLoading = false;
private boolean cacheInMemory = false;
private boolean cacheOnDisc = false;
private ImageScaleType imageScaleType;
private Options decodingOptions;
private int delayBeforeLoading;
private Object extraForDownloader;
private BitmapProcessor preProcessor;
private BitmapProcessor postProcessor;
private BitmapDisplayer displayer;
private Handler handler;
public Builder() {
this.imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
this.decodingOptions = new Options();
this.delayBeforeLoading = 0;
this.extraForDownloader = null;
this.preProcessor = null;
this.postProcessor = null;
this.displayer = DefaultConfigurationFactory.createBitmapDisplayer();
this.handler = null;
this.decodingOptions.inPurgeable = true;
this.decodingOptions.inInputShareable = true;
}
public DisplayImageOptions.Builder showStubImage(int stubImageRes) {
this.stubImage = stubImageRes;
return this;
}
public DisplayImageOptions.Builder showImageForEmptyUri(int imageRes) {
this.imageForEmptyUri = imageRes;
return this;
}
public DisplayImageOptions.Builder showImageOnFail(int imageRes) {
this.imageOnFail = imageRes;
return this;
}
/** @deprecated */
public DisplayImageOptions.Builder resetViewBeforeLoading() {
this.resetViewBeforeLoading = true;
return this;
}
public DisplayImageOptions.Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) {
this.resetViewBeforeLoading = resetViewBeforeLoading;
return this;
}
/** @deprecated */
public DisplayImageOptions.Builder cacheInMemory() {
this.cacheInMemory = true;
return this;
}
public DisplayImageOptions.Builder cacheInMemory(boolean cacheInMemory) {
this.cacheInMemory = cacheInMemory;
return this;
}
/** @deprecated */
public DisplayImageOptions.Builder cacheOnDisc() {
this.cacheOnDisc = true;
return this;
}
public DisplayImageOptions.Builder cacheOnDisc(boolean cacheOnDisc) {
this.cacheOnDisc = cacheOnDisc;
return this;
}
public DisplayImageOptions.Builder imageScaleType(ImageScaleType imageScaleType) {
this.imageScaleType = imageScaleType;
return this;
}
public DisplayImageOptions.Builder bitmapConfig(Config bitmapConfig) {
if (bitmapConfig == null) {
throw new IllegalArgumentException("bitmapConfig can't be null");
} else {
this.decodingOptions.inPreferredConfig = bitmapConfig;
return this;
}
}
public DisplayImageOptions.Builder decodingOptions(Options decodingOptions) {
if (decodingOptions == null) {
throw new IllegalArgumentException("decodingOptions can't be null");
} else {
this.decodingOptions = decodingOptions;
return this;
}
}
public DisplayImageOptions.Builder delayBeforeLoading(int delayInMillis) {
this.delayBeforeLoading = delayInMillis;
return this;
}
public DisplayImageOptions.Builder extraForDownloader(Object extra) {
this.extraForDownloader = extra;
return this;
}
public DisplayImageOptions.Builder preProcessor(BitmapProcessor preProcessor) {
this.preProcessor = preProcessor;
return this;
}
public DisplayImageOptions.Builder postProcessor(BitmapProcessor postProcessor) {
this.postProcessor = postProcessor;
return this;
}
public DisplayImageOptions.Builder displayer(BitmapDisplayer displayer) {
if (displayer == null) {
throw new IllegalArgumentException("displayer can't be null");
} else {
this.displayer = displayer;
return this;
}
}
public DisplayImageOptions.Builder handler(Handler handler) {
this.handler = handler;
return this;
}
public DisplayImageOptions.Builder cloneFrom(DisplayImageOptions options) {
this.stubImage = options.stubImage;
this.imageForEmptyUri = options.imageForEmptyUri;
this.imageOnFail = options.imageOnFail;
this.resetViewBeforeLoading = options.resetViewBeforeLoading;
this.cacheInMemory = options.cacheInMemory;
this.cacheOnDisc = options.cacheOnDisc;
this.imageScaleType = options.imageScaleType;
this.decodingOptions = options.decodingOptions;
this.delayBeforeLoading = options.delayBeforeLoading;
this.extraForDownloader = options.extraForDownloader;
this.preProcessor = options.preProcessor;
this.postProcessor = options.postProcessor;
this.displayer = options.displayer;
this.handler = options.handler;
return this;
}
public DisplayImageOptions build() {
return new DisplayImageOptions(this);
}
}
}
ImageLoaderConfiguration源码
public final class ImageLoaderConfiguration {
final Context context;
final int maxImageWidthForMemoryCache;
final int maxImageHeightForMemoryCache;
final int maxImageWidthForDiscCache;
final int maxImageHeightForDiscCache;
final CompressFormat imageCompressFormatForDiscCache;
final int imageQualityForDiscCache;
final BitmapProcessor processorForDiscCache;
final Executor taskExecutor;
final Executor taskExecutorForCachedImages;
final boolean customExecutor;
final boolean customExecutorForCachedImages;
final int threadPoolSize;
final int threadPriority;
final QueueProcessingType tasksProcessingType;
final MemoryCacheAware<String, Bitmap> memoryCache;
final DiscCacheAware discCache;
final ImageDownloader downloader;
final ImageDecoder decoder;
final DisplayImageOptions defaultDisplayImageOptions;
final boolean loggingEnabled;
final DiscCacheAware reserveDiscCache;
final ImageDownloader networkDeniedDownloader;
final ImageDownloader slowNetworkDownloader;
private ImageLoaderConfiguration(ImageLoaderConfiguration.Builder builder) {
this.context = builder.context;
this.maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
this.maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
this.maxImageWidthForDiscCache = builder.maxImageWidthForDiscCache;
this.maxImageHeightForDiscCache = builder.maxImageHeightForDiscCache;
this.imageCompressFormatForDiscCache = builder.imageCompressFormatForDiscCache;
this.imageQualityForDiscCache = builder.imageQualityForDiscCache;
this.processorForDiscCache = builder.processorForDiscCache;
this.taskExecutor = builder.taskExecutor;
this.taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
this.threadPoolSize = builder.threadPoolSize;
this.threadPriority = builder.threadPriority;
this.tasksProcessingType = builder.tasksProcessingType;
this.discCache = builder.discCache;
this.memoryCache = builder.memoryCache;
this.defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
this.loggingEnabled = builder.loggingEnabled;
this.downloader = builder.downloader;
this.decoder = builder.decoder;
this.customExecutor = builder.customExecutor;
this.customExecutorForCachedImages = builder.customExecutorForCachedImages;
this.networkDeniedDownloader = new NetworkDeniedImageDownloader(this.downloader);
this.slowNetworkDownloader = new SlowNetworkImageDownloader(this.downloader);
this.reserveDiscCache = DefaultConfigurationFactory.createReserveDiscCache(this.context);
}
public static ImageLoaderConfiguration createDefault(Context context) {
return (new ImageLoaderConfiguration.Builder(context)).build();
}
public static class Builder {
private static final String WARNING_OVERLAP_DISC_CACHE_PARAMS = "discCache(), discCacheSize() and discCacheFileCount calls overlap each other";
private static final String WARNING_OVERLAP_DISC_CACHE_NAME_GENERATOR = "discCache() and discCacheFileNameGenerator() calls overlap each other";
private static final String WARNING_OVERLAP_MEMORY_CACHE = "memoryCache() and memoryCacheSize() calls overlap each other";
private static final String WARNING_OVERLAP_EXECUTOR = "threadPoolSize(), threadPriority() and tasksProcessingOrder() calls can overlap taskExecutor() and taskExecutorForCachedImages() calls.";
public static final int DEFAULT_THREAD_POOL_SIZE = 3;
public static final int DEFAULT_THREAD_PRIORITY = 4;
public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE;
private Context context;
private int maxImageWidthForMemoryCache = 0;
private int maxImageHeightForMemoryCache = 0;
private int maxImageWidthForDiscCache = 0;
private int maxImageHeightForDiscCache = 0;
private CompressFormat imageCompressFormatForDiscCache = null;
private int imageQualityForDiscCache = 0;
private BitmapProcessor processorForDiscCache = null;
private Executor taskExecutor = null;
private Executor taskExecutorForCachedImages = null;
private boolean customExecutor = false;
private boolean customExecutorForCachedImages = false;
private int threadPoolSize = 3;
private int threadPriority = 4;
private boolean denyCacheImageMultipleSizesInMemory = false;
private QueueProcessingType tasksProcessingType;
private int memoryCacheSize;
private int discCacheSize;
private int discCacheFileCount;
private MemoryCacheAware<String, Bitmap> memoryCache;
private DiscCacheAware discCache;
private FileNameGenerator discCacheFileNameGenerator;
private ImageDownloader downloader;
private ImageDecoder decoder;
private DisplayImageOptions defaultDisplayImageOptions;
private boolean loggingEnabled;
public Builder(Context context) {
this.tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE;
this.memoryCacheSize = 0;
this.discCacheSize = 0;
this.discCacheFileCount = 0;
this.memoryCache = null;
this.discCache = null;
this.discCacheFileNameGenerator = null;
this.downloader = null;
this.defaultDisplayImageOptions = null;
this.loggingEnabled = false;
this.context = context.getApplicationContext();
}
public ImageLoaderConfiguration.Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) {
this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache;
this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache;
return this;
}
public ImageLoaderConfiguration.Builder discCacheExtraOptions(int maxImageWidthForDiscCache, int maxImageHeightForDiscCache, CompressFormat compressFormat, int compressQuality, BitmapProcessor processorForDiscCache) {
this.maxImageWidthForDiscCache = maxImageWidthForDiscCache;
this.maxImageHeightForDiscCache = maxImageHeightForDiscCache;
this.imageCompressFormatForDiscCache = compressFormat;
this.imageQualityForDiscCache = compressQuality;
this.processorForDiscCache = processorForDiscCache;
return this;
}
public ImageLoaderConfiguration.Builder taskExecutor(Executor executor) {
if (this.threadPoolSize != 3 || this.threadPriority != 4 || this.tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
L.w("threadPoolSize(), threadPriority() and tasksProcessingOrder() calls can overlap taskExecutor() and taskExecutorForCachedImages() calls.", new Object[0]);
}
this.taskExecutor = executor;
return this;
}
public ImageLoaderConfiguration.Builder taskExecutorForCachedImages(Executor executorForCachedImages) {
if (this.threadPoolSize != 3 || this.threadPriority != 4 || this.tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
L.w("threadPoolSize(), threadPriority() and tasksProcessingOrder() calls can overlap taskExecutor() and taskExecutorForCachedImages() calls.", new Object[0]);
}
this.taskExecutorForCachedImages = executorForCachedImages;
return this;
}
public ImageLoaderConfiguration.Builder threadPoolSize(int threadPoolSize) {
if (this.taskExecutor != null || this.taskExecutorForCachedImages != null) {
L.w("threadPoolSize(), threadPriority() and tasksProcessingOrder() calls can overlap taskExecutor() and taskExecutorForCachedImages() calls.", new Object[0]);
}
this.threadPoolSize = threadPoolSize;
return this;
}
public ImageLoaderConfiguration.Builder threadPriority(int threadPriority) {
if (this.taskExecutor != null || this.taskExecutorForCachedImages != null) {
L.w("threadPoolSize(), threadPriority() and tasksProcessingOrder() calls can overlap taskExecutor() and taskExecutorForCachedImages() calls.", new Object[0]);
}
if (threadPriority < 1) {
this.threadPriority = 1;
} else if (threadPriority > 10) {
boolean threadPriority1 = true;
} else {
this.threadPriority = threadPriority;
}
return this;
}
public ImageLoaderConfiguration.Builder denyCacheImageMultipleSizesInMemory() {
this.denyCacheImageMultipleSizesInMemory = true;
return this;
}
public ImageLoaderConfiguration.Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) {
if (this.taskExecutor != null || this.taskExecutorForCachedImages != null) {
L.w("threadPoolSize(), threadPriority() and tasksProcessingOrder() calls can overlap taskExecutor() and taskExecutorForCachedImages() calls.", new Object[0]);
}
this.tasksProcessingType = tasksProcessingType;
return this;
}
public ImageLoaderConfiguration.Builder memoryCacheSize(int memoryCacheSize) {
if (memoryCacheSize <= 0) {
throw new IllegalArgumentException("memoryCacheSize must be a positive number");
} else {
if (this.memoryCache != null) {
L.w("memoryCache() and memoryCacheSize() calls overlap each other", new Object[0]);
}
this.memoryCacheSize = memoryCacheSize;
return this;
}
}
public ImageLoaderConfiguration.Builder memoryCacheSizePercentage(int avaialbleMemoryPercent) {
if (avaialbleMemoryPercent > 0 && avaialbleMemoryPercent < 100) {
if (this.memoryCache != null) {
L.w("memoryCache() and memoryCacheSize() calls overlap each other", new Object[0]);
}
long availableMemory = Runtime.getRuntime().maxMemory();
this.memoryCacheSize = (int)((float)availableMemory * ((float)avaialbleMemoryPercent / 100.0F));
return this;
} else {
throw new IllegalArgumentException("avaialbleMemoryPercent must be in range (0 < % < 100)");
}
}
public ImageLoaderConfiguration.Builder memoryCache(MemoryCacheAware<String, Bitmap> memoryCache) {
if (this.memoryCacheSize != 0) {
L.w("memoryCache() and memoryCacheSize() calls overlap each other", new Object[0]);
}
this.memoryCache = memoryCache;
return this;
}
public ImageLoaderConfiguration.Builder discCacheSize(int maxCacheSize) {
if (maxCacheSize <= 0) {
throw new IllegalArgumentException("maxCacheSize must be a positive number");
} else {
if (this.discCache != null || this.discCacheFileCount > 0) {
L.w("discCache(), discCacheSize() and discCacheFileCount calls overlap each other", new Object[0]);
}
this.discCacheSize = maxCacheSize;
return this;
}
}
public ImageLoaderConfiguration.Builder discCacheFileCount(int maxFileCount) {
if (maxFileCount <= 0) {
throw new IllegalArgumentException("maxFileCount must be a positive number");
} else {
if (this.discCache != null || this.discCacheSize > 0) {
L.w("discCache(), discCacheSize() and discCacheFileCount calls overlap each other", new Object[0]);
}
this.discCacheSize = 0;
this.discCacheFileCount = maxFileCount;
return this;
}
}
public ImageLoaderConfiguration.Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
if (this.discCache != null) {
L.w("discCache() and discCacheFileNameGenerator() calls overlap each other", new Object[0]);
}
this.discCacheFileNameGenerator = fileNameGenerator;
return this;
}
public ImageLoaderConfiguration.Builder imageDownloader(ImageDownloader imageDownloader) {
this.downloader = imageDownloader;
return this;
}
public ImageLoaderConfiguration.Builder imageDecoder(ImageDecoder imageDecoder) {
this.decoder = imageDecoder;
return this;
}
public ImageLoaderConfiguration.Builder discCache(DiscCacheAware discCache) {
if (this.discCacheSize > 0 || this.discCacheFileCount > 0) {
L.w("discCache(), discCacheSize() and discCacheFileCount calls overlap each other", new Object[0]);
}
if (this.discCacheFileNameGenerator != null) {
L.w("discCache() and discCacheFileNameGenerator() calls overlap each other", new Object[0]);
}
this.discCache = discCache;
return this;
}
public ImageLoaderConfiguration.Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {
this.defaultDisplayImageOptions = defaultDisplayImageOptions;
return this;
}
public ImageLoaderConfiguration.Builder enableLogging() {
this.loggingEnabled = true;
return this;
}
public ImageLoaderConfiguration build() {
this.initEmptyFiledsWithDefaultValues();
return new ImageLoaderConfiguration(this);
}
private void initEmptyFiledsWithDefaultValues() {
if (this.taskExecutor == null) {
this.taskExecutor = DefaultConfigurationFactory.createExecutor(this.threadPoolSize, this.threadPriority, this.tasksProcessingType);
} else {
this.customExecutor = true;
}
if (this.taskExecutorForCachedImages == null) {
this.taskExecutorForCachedImages = DefaultConfigurationFactory.createExecutor(this.threadPoolSize, this.threadPriority, this.tasksProcessingType);
} else {
this.customExecutorForCachedImages = true;
}
if (this.discCache == null) {
if (this.discCacheFileNameGenerator == null) {
this.discCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
}
this.discCache = DefaultConfigurationFactory.createDiscCache(this.context, this.discCacheFileNameGenerator, this.discCacheSize, this.discCacheFileCount);
}
if (this.memoryCache == null) {
this.memoryCache = DefaultConfigurationFactory.createMemoryCache(this.memoryCacheSize);
}
if (this.denyCacheImageMultipleSizesInMemory) {
this.memoryCache = new FuzzyKeyMemoryCache(this.memoryCache, MemoryCacheUtil.createFuzzyKeyComparator());
}
if (this.downloader == null) {
this.downloader = DefaultConfigurationFactory.createImageDownloader(this.context);
}
if (this.decoder == null) {
this.decoder = DefaultConfigurationFactory.createImageDecoder(this.loggingEnabled);
}
if (this.defaultDisplayImageOptions == null) {
this.defaultDisplayImageOptions = DisplayImageOptions.createSimple();
}
}
static {
DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO;
}
}
}
ImageLoader源码
public class ImageLoader {
public static final String TAG = ImageLoader.class.getSimpleName();
static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration";
static final String LOG_DESTROY = "Destroy ImageLoader";
static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]";
private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. To re-init ImageLoader with new configuration call ImageLoader.destroy() at first.";
private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)";
private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using";
private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null";
private ImageLoaderConfiguration configuration;
private ImageLoaderEngine engine;
private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener();
private final BitmapDisplayer fakeBitmapDisplayer = new FakeBitmapDisplayer();
private static volatile ImageLoader instance;
public static ImageLoader getInstance() {
if (instance == null) {
Class var0 = ImageLoader.class;
synchronized(ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}
protected ImageLoader() {
}
public synchronized void init(ImageLoaderConfiguration configuration) {
if (configuration == null) {
throw new IllegalArgumentException("ImageLoader configuration can not be initialized with null");
} else {
if (this.configuration == null) {
if (configuration.loggingEnabled) {
L.d("Initialize ImageLoader with configuration", new Object[0]);
}
this.engine = new ImageLoaderEngine(configuration);
this.configuration = configuration;
} else {
L.w("Try to initialize ImageLoader which had already been initialized before. To re-init ImageLoader with new configuration call ImageLoader.destroy() at first.", new Object[0]);
}
}
}
public boolean isInited() {
return this.configuration != null;
}
public void displayImage(String uri, ImageView imageView) {
this.displayImage(uri, imageView, (DisplayImageOptions)null, (ImageLoadingListener)null);
}
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
this.displayImage(uri, imageView, options, (ImageLoadingListener)null);
}
public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) {
this.displayImage(uri, imageView, (DisplayImageOptions)null, listener);
}
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) {
this.checkConfiguration();
if (imageView == null) {
throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)");
} else {
if (listener == null) {
listener = this.emptyListener;
}
if (options == null) {
options = this.configuration.defaultDisplayImageOptions;
}
if (TextUtils.isEmpty(uri)) {
this.engine.cancelDisplayTaskFor(imageView);
listener.onLoadingStarted(uri, imageView);
if (options.shouldShowImageForEmptyUri()) {
imageView.setImageResource(options.getImageForEmptyUri());
} else {
imageView.setImageDrawable((Drawable)null);
}
listener.onLoadingComplete(uri, imageView, (Bitmap)null);
} else {
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache);
String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);
this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey);
listener.onLoadingStarted(uri, imageView);
Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);
ImageLoadingInfo imageLoadingInfo;
if (bmp != null && !bmp.isRecycled()) {
if (this.configuration.loggingEnabled) {
L.i("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
}
if (options.shouldPostProcess()) {
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask);
} else {
options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageView, bmp);
}
} else {
if (options.shouldShowStubImage()) {
imageView.setImageResource(options.getStubImage());
} else if (options.isResetViewBeforeLoading()) {
imageView.setImageDrawable((Drawable)null);
}
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask);
}
}
}
}
public void loadImage(String uri, ImageLoadingListener listener) {
this.loadImage(uri, (ImageSize)null, (DisplayImageOptions)null, listener);
}
public void loadImage(String uri, ImageSize minImageSize, ImageLoadingListener listener) {
this.loadImage(uri, minImageSize, (DisplayImageOptions)null, listener);
}
public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) {
this.loadImage(uri, (ImageSize)null, options, listener);
}
public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener) {
this.checkConfiguration();
if (targetImageSize == null) {
targetImageSize = new ImageSize(this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache);
}
if (options == null) {
options = this.configuration.defaultDisplayImageOptions;
}
DisplayImageOptions optionsWithFakeDisplayer;
if (options.getDisplayer() instanceof FakeBitmapDisplayer) {
optionsWithFakeDisplayer = options;
} else {
optionsWithFakeDisplayer = (new Builder()).cloneFrom(options).displayer(this.fakeBitmapDisplayer).build();
}
ImageView fakeImage = new ImageView(this.configuration.context);
fakeImage.setLayoutParams(new LayoutParams(targetImageSize.getWidth(), targetImageSize.getHeight()));
fakeImage.setScaleType(ScaleType.CENTER_CROP);
this.displayImage(uri, fakeImage, optionsWithFakeDisplayer, listener);
}
private void checkConfiguration() {
if (this.configuration == null) {
throw new IllegalStateException("ImageLoader must be init with configuration before using");
}
}
public MemoryCacheAware<String, Bitmap> getMemoryCache() {
this.checkConfiguration();
return this.configuration.memoryCache;
}
public void clearMemoryCache() {
this.checkConfiguration();
this.configuration.memoryCache.clear();
}
public DiscCacheAware getDiscCache() {
this.checkConfiguration();
return this.configuration.discCache;
}
public void clearDiscCache() {
this.checkConfiguration();
this.configuration.discCache.clear();
}
public String getLoadingUriForView(ImageView imageView) {
return this.engine.getLoadingUriForView(imageView);
}
public void cancelDisplayTask(ImageView imageView) {
this.engine.cancelDisplayTaskFor(imageView);
}
public void denyNetworkDownloads(boolean denyNetworkDownloads) {
this.engine.denyNetworkDownloads(denyNetworkDownloads);
}
public void handleSlowNetwork(boolean handleSlowNetwork) {
this.engine.handleSlowNetwork(handleSlowNetwork);
}
public void pause() {
this.engine.pause();
}
public void resume() {
this.engine.resume();
}
public void stop() {
this.engine.stop();
}
public void destroy() {
if (this.configuration != null && this.configuration.loggingEnabled) {
L.d("Destroy ImageLoader", new Object[0]);
}
this.stop();
this.engine = null;
this.configuration = null;
}
}
二.分析
从ImageLoader的源码中可以看出loadImage(...)方法有四个重载方法
loadImage(String uri, ImageLoadingListener listener)
loadImage(String uri, ImageSize minImageSize, ImageLoadingListener listener)
loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener)
loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener)
其中前三个方法中只是调用了第四个方法而第四个方法有调用了displayImage(...)方法。
所以loadImage(...)方法其实就是displayImage(...)方法
displayImage(...)方法方法也有四个重载方法
displayImage(String uri, ImageView imageView)
displayImage(String uri, ImageView imageView, DisplayImageOptions options)
displayImage(String uri, ImageView imageView, ImageLoadingListener listener)
displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)
而和loadImage(...)方法一样displayImage(...)方法的前三个方法也是只是调用了第四个方法。
也就是说universal-image-loader框架加载图片时只要用到的就是
displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) 方法
displayImage(...)四个参数方法的源码如下
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) {
this.checkConfiguration();
if (imageView == null) {
throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)");
} else {
if (listener == null) {
listener = this.emptyListener;
}
if (options == null) {
options = this.configuration.defaultDisplayImageOptions;
}
if (TextUtils.isEmpty(uri)) {
this.engine.cancelDisplayTaskFor(imageView);
listener.onLoadingStarted(uri, imageView);
if (options.shouldShowImageForEmptyUri()) {
imageView.setImageResource(options.getImageForEmptyUri());
} else {
imageView.setImageDrawable((Drawable)null);
}
listener.onLoadingComplete(uri, imageView, (Bitmap)null);
} else {
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache);
String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);
this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey);
listener.onLoadingStarted(uri, imageView);
Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);
ImageLoadingInfo imageLoadingInfo;
if (bmp != null && !bmp.isRecycled()) {
if (this.configuration.loggingEnabled) {
L.i("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
}
if (options.shouldPostProcess()) {
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask);
} else {
options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageView, bmp);
}
} else {
if (options.shouldShowStubImage()) {
imageView.setImageResource(options.getStubImage());
} else if (options.isResetViewBeforeLoading()) {
imageView.setImageDrawable((Drawable)null);
}
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask);
}
}
}
}
当传递的imageview为空时抛出“传递参数有误,文件找不到”的异常
if(imageView == null) {
throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)");
}
当传递的ImageLoadingListener接口对象或是SimpleImageLoadingListener(ImageLoadingListener接口的实现类 常用)为空时 监听为自己类创建的
private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener();
if(listener ==null) {
listener = this.emptyListener;
}
当传递的DisplayImageOptions 对象为空时 对象为自己类创建
if(options == null) {
options = this.configuration.defaultDisplayImageOptions;
}
当图片路径为空时
if(TextUtils.isEmpty(uri)) {
this.engine.cancelDisplayTaskFor(imageView);
listener.onLoadingStarted(uri, imageView);
if(options.shouldShowImageForEmptyUri()) {
imageView.setImageResource(options.getImageForEmptyUri());
} else {
imageView.setImageDrawable((Drawable)null);
}
listener.onLoadingComplete(uri, imageView, (Bitmap)null);
}
即此时 如果DisplayImageOptions对象设置了.showImageForEmptyUri(R.drawable.companylogo_default)
及路径为空时显示默认图片 此时就显示设置的图片
如果没设置图片显示为空
最后调用ImageLoadingListener接口对象或是SimpleImageLoadingListener(ImageLoadingListener接口的实现类 常用)onLoadingComplete方法
当路径不为空时(即所有条件都合适)
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache);
String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);
this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey);
listener.onLoadingStarted(uri, imageView);
Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);
ImageLoadingInfo imageLoadingInfo;
if(bmp != null && !bmp.isRecycled()) {
if(this.configuration.loggingEnabled) {
L.i("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
}
if(options.shouldPostProcess()) {
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask1);
} else {
options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageView, bmp);
}
} else {
if(options.shouldShowStubImage()) {
imageView.setImageResource(options.getStubImage());
} else if(options.isResetViewBeforeLoading()) {
imageView.setImageDrawable((Drawable)null);
}
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask);
}
先从缓存中找
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache);
String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);
this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey);
listener.onLoadingStarted(uri, imageView);
Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);
如果缓存中的bitmap不为空且没有被回收
if(options.shouldPostProcess()) {
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask1);
} else {
options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageView, bmp);
}
DisplayImageOptions类的display()方法 并调用onLoadingComplete()方法
否则 bitmap为空或是已经被回收
if(options.shouldShowStubImage()) {
imageView.setImageResource(options.getStubImage());
} else if(options.isResetViewBeforeLoading()) {
imageView.setImageDrawable((Drawable)null);
}
imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());
this.engine.submit(displayTask);
三.初始化
1.框架初始化(全局)
public class CliniciansApplication extends Application {
...
public static ImageLoader imageLoader = ImageLoader.getInstance();//ImageLoader图片加载框架
...
}
2.ImageLoaderConfiguration
ImageLoaderConfiguration的配置主要是全局性的配置,主要有线程类、缓存大小、磁盘大小、图片下载与解析、日志方面的配置。
ImageLoaderConfiguration初始化
默认方式
ImageLoaderConfiguration configuration=ImageLoaderConfiguration.createDefault(context);
自定义方式
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration
.Builder(this)
.memoryCacheExtraOptions(480, 800)//内存缓存文件的最大长宽
.discCacheExtraOptions(480, 800, null)//本地缓存的详细信息
.threadPoolSize(3)//线程池内加载的数量
.threadPriority(Thread.NORM_PRIORITY - 2)//设置当前线程的优先级
.tasksProcessingOrder(QueueProcessingType.FIFO)//设置任务的处理顺序
.denyCacheImageMultipleSizesInMemory()//防止内存中图片重复
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))//设置自己的内存缓存大小2M
.memoryCacheSize(2 * 1024 * 1024).memoryCacheSizePercentage(13)//内存缓存百分比
.discCache(new UnlimitedDiscCache(new File(sdPath + "/XXX/XXX")))//设置缓存的图片在sdcard中的位置
.discCacheSize(50 * 1024 * 1024)//硬盘缓存大小 50M.discCacheFileCount(100)//硬盘缓存文件个数
.discCacheFileNameGenerator(new Md5FileNameGenerator())//md5加密的方式,或new HashCodeFileNameGenerator()
.imageDownloader(new BaseImageDownloader(this))
.imageDecoder(new BaseImageDecoder(true))//图片解码
.defaultDisplayImageOptions(getOption())//是否使用默认的图片加载配置,null表示不使用
.writeDebugLogs()
.build();
一般使用默认方式即可
ImageLoaderConfiguration configuration=ImageLoaderConfiguration.createDefault(context);
3.DisplayImageOptions
options=new DisplayImageOptions.Builder()
.showStubImage(R.drawable.imageviewattachmentloading_background)//加载过程中显示内容
.showImageForEmptyUri(R.mipmap.followup_def)//路径为空显示内容
.showImageOnFail(R.mipmap.followup_def)//下载失败显示内容
.cacheInMemory(true)//内存缓存
.cacheOnDisc(true)//磁盘缓存
.bitmapConfig(Bitmap.Config.RGB_565)//图片格式
.build();
4.ImageLoaderConfiguration默认内存缓存使用的是LruMemoryCache。LruMemoryCache缓存的是bitmap的强引用
LruMemoryCache源码
public class LruMemoryCache implements MemoryCacheAware<String, Bitmap> {
private final LinkedHashMap<String, Bitmap> map;
private final int maxSize;
private int size;
public LruMemoryCache(int maxSize) {
if(maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
} else {
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75F, true);
}
}
public final Bitmap get(String key) {
if(key == null) {
throw new NullPointerException("key == null");
} else {
synchronized(this) {
return (Bitmap)this.map.get(key);
}
}
}
public final boolean put(String key, Bitmap value) {
if(key != null && value != null) {
synchronized(this) {
this.size += this.sizeOf(key, value);
Bitmap previous = (Bitmap)this.map.put(key, value);
if(previous != null) {
this.size -= this.sizeOf(key, previous);
}
}
this.trimToSize(this.maxSize);
return true;
} else {
throw new NullPointerException("key == null || value == null");
}
}
private void trimToSize(int maxSize) {
while(true) {
synchronized(this) {
if(this.size < 0 || this.map.isEmpty() && this.size != 0) {
throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
if(this.size > maxSize && !this.map.isEmpty()) {
Entry toEvict = (Entry)this.map.entrySet().iterator().next();
if(toEvict != null) {
String key = (String)toEvict.getKey();
Bitmap value = (Bitmap)toEvict.getValue();
this.map.remove(key);
this.size -= this.sizeOf(key, value);
continue;
}
}
return;
}
}
}
public final void remove(String key) {
if(key == null) {
throw new NullPointerException("key == null");
} else {
synchronized(this) {
Bitmap previous = (Bitmap)this.map.remove(key);
if(previous != null) {
this.size -= this.sizeOf(key, previous);
}
}
}
}
public Collection<String> keys() {
synchronized(this) {
return new HashSet(this.map.keySet());
}
}
public void clear() {
this.trimToSize(-1);
}
private int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
public final synchronized String toString() {
return String.format("LruCache[maxSize=%d]", new Object[]{Integer.valueOf(this.maxSize)});
}
附1:universal-image-loader框架GitHub链接
https://github.com/nostra13/Android-Universal-Image-Loader
附2:此框架具体使用
Android 图片库之universal-image-loader框架详解(中)
Android 图片库之universal-image-loader框架详解(下)
附3:几种图片框架对比图