版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a820703048/article/details/79201111
背景
接着上一篇的内容,我们接着看ImageLoader的核心代码篇,上一篇主要是看ImageLoader的内存优化,主要包括磁盘缓存和内存缓存,还有就是内存的缓存策略,这一篇,我们重点来看一下ImageLoader是如何使用这些内存和进行图片的展示和图片处理和下载的。
一、code组织框架
首先我们先看code包下面的组织框架,然后我们再每个类去分析,最后我们全部串起来学习。核心代码篇和上一篇缓存篇不一样,核心代码量较多,但是我们博客篇幅有限,我们只能按重要程度,将大部分进行讲解,不多说先上截图。
二 、详细代码及类讲解
(1)ImageLoader
这个类可能是开发者最先接触或者说印象最深刻的类了,我们来看一下我们是怎么接触它的,我们来看一下它的最简单使用和源码的实现。
//创建默认的ImageLoader配置参数
ImageLoaderConfiguration configuration = ImageLoaderConfiguration
.createDefault(this);
ImageLoader.getInstance().init(configuration);
ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);
/**
* 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 ImageLoadingListener defaultListener = new SimpleImageLoadingListener();
private volatile static ImageLoader instance;
/**
* 先来个单例模式
*/
public static ImageLoader getInstance() {
if (instance == null) {
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(ERROR_INIT_CONFIG_WITH_NULL);
}
if (this.configuration == null) {
engine = new ImageLoaderEngine(configuration);
this.configuration = configuration;
} else {
}
}
/**
* 判断是否被初始化
*/
public boolean isInited() {
return configuration != null;
}
/**
* 多级调用 将imageview转化为imageAware
*/
public void displayImage(String uri, ImageAware imageAware) {
displayImage(uri, imageAware, null, null, null);
}
/**
* 多级调用加监听
*/
public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) {
displayImage(uri, imageAware, null, listener, null);
}
/**
* 调用加展示参数
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) {
displayImage(uri, imageAware, options, null, null);
}
/**
* 进行多级调用 两者都有
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener) {
displayImage(uri, imageAware, options, listener, null);
}
/**
* 这个参数有加了参数和监听器和进度条监听器
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
displayImage(uri, imageAware, options, null, listener, progressListener);
}
/**
* 所有方法最终走到这里
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
checkConfiguration();//检查 是否有配置文件
if (imageAware == null) {//都是判断了
throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
}
if (listener == null) {
listener = defaultListener;
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
if (TextUtils.isEmpty(uri)) {//第一行 判断uri是否是空 如果是空 直接取消engine中的任务
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());//然后调用listener的start
if (options.shouldShowImageForEmptyUri()) {//由于 uri为空 如果设置了需要设置空的图像那么直接设置 图像是 空的时候需要设置的图像即可 如果没设置,直接不显示就好
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
imageAware.setImageDrawable(null);
}//之后调用 complete 回调 返回 这是uri为空的情况 不需要做太多操作 也不需要缓存
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}
if (targetSize == null) {//像的大小 设置是空 那么根据控件设置的大小 设置 要展示图片的大小
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
}
//之后 根据 uri和目标的大小 生成一个key 并把 这个任务放入 engine 的集合中
// 回调 started方法
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
// 从内存缓存中根据key取bitmap
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
//如果存在 并且没被回收
if (bmp != null && !bmp.isRecycled()) {
//如果设置了 postProcess 执行 默认没设置 设置这个可以提前对图片进行某些处理
if (options.shouldPostProcess()) {
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
} else {
//不需要 在展示图片之前处理图片时,那么就直接使用 displaywe 对 图片进行 展示 并回调complete函数
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
} else {//如果不存在内存缓存中 或者已经被回收了
if (options.shouldShowImageOnLoading()) {//如果加载时需要显示图片 那么设置 否则 不设置图片
imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
} else if (options.isResetViewBeforeLoading()) {
imageAware.setImageDrawable(null);
}
//然后 设置正在加载时的信息 ImageLoadingInfo 和 任务LoadAndDisplayImageTask
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {//根据是否同步 执行任务
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}
/**
* 用户直接在外面调用的,里面将他转成了ImageViewAware
*/
public void displayImage(String uri, ImageView imageView) {
displayImage(uri, new ImageViewAware(imageView), null, null, null);
}
/**
* 展示图片添加图片大小
*/
public void displayImage(String uri, ImageView imageView, ImageSize targetImageSize) {
displayImage(uri, new ImageViewAware(imageView), null, targetImageSize, null, null);
}
/**
* 展示图片加参数
*/
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
displayImage(uri, new ImageViewAware(imageView), options, null, null);
}
/**
* 图片加监听
*/
public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) {
displayImage(uri, new ImageViewAware(imageView), null, listener, null);
}
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
ImageLoadingListener listener) {
displayImage(uri, imageView, options, listener, null);
}
/**
* 展示图片加监听加进度条
*/
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener);
}
/**
* 展示图片加监听
*/
public void loadImage(String uri, ImageLoadingListener listener) {
loadImage(uri, null, null, listener, null);
}
/**
* 展示图片加内存限制加监听
*/
public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) {
loadImage(uri, targetImageSize, null, listener, null);
}
/**
*/
public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) {
loadImage(uri, null, options, listener, null);
}
/**
* 展示图片加目标大小
*/
public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
ImageLoadingListener listener) {
loadImage(uri, targetImageSize, options, listener, null);
}
/**
* 展示图片 所有参数都加
*/
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);
}
/**
* 加载图片 异步的
*/
public Bitmap loadImageSync(String uri) {
return loadImageSync(uri, null, null);
}
/**
* 加载图片异步加参数
*/
public Bitmap loadImageSync(String uri, DisplayImageOptions options) {
return loadImageSync(uri, null, options);
}
/**
* 加载图片限制图片大小
*/
public Bitmap loadImageSync(String uri, ImageSize targetImageSize) {
return loadImageSync(uri, targetImageSize, null);
}
/**
* 加载图片异步
*/
public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) {
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build();
SyncImageLoadingListener listener = new SyncImageLoadingListener();
loadImage(uri, targetImageSize, options, listener);
return listener.getLoadedBitmap();
}
/**
* Checks if ImageLoader's configuration was initialized
* 检查配置
*/
private void checkConfiguration() {
if (configuration == null) {
throw new IllegalStateException(ERROR_NOT_INIT);
}
}
/**
* 设置监听器.
*/
public void setDefaultLoadingListener(ImageLoadingListener listener) {
defaultListener = listener == null ? new SimpleImageLoadingListener() : listener;
}
/**
* Returns memory cache
* 返回内存策略
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public MemoryCache getMemoryCache() {
checkConfiguration();
return configuration.memoryCache;
}
/**
* Clears memory cache
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void clearMemoryCache() {
checkConfiguration();
configuration.memoryCache.clear();
}
/**
* Returns disk cache
* 返回磁盘缓存
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @deprecated Use {@link #getDiskCache()} instead
*/
@Deprecated
public DiskCache getDiscCache() {
return getDiskCache();
}
/**
* Returns disk cache
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public DiskCache getDiskCache() {
checkConfiguration();
return configuration.diskCache;
}
/**
* Clears disk cache.
*/
@Deprecated
public void clearDiscCache() {
clearDiskCache();
}
/**
* Clears disk cache.
*/
public void clearDiskCache() {
checkConfiguration();
configuration.diskCache.clear();
}
/**
* Returns URI of image which is loading at this moment into passed
*/
public String getLoadingUriForView(ImageAware imageAware) {
return engine.getLoadingUriForView(imageAware);
}
/**
* Returns URI of image which is loading at this moment into passed
*/
public String getLoadingUriForView(ImageView imageView) {
return engine.getLoadingUriForView(new ImageViewAware(imageView));
}
/**
* Cancel the task of loading and displaying image for passed
* which display task will be cancelled
*/
public void cancelDisplayTask(ImageAware imageAware) {
engine.cancelDisplayTaskFor(imageAware);
}
/**
*/
public void cancelDisplayTask(ImageView imageView) {
engine.cancelDisplayTaskFor(new ImageViewAware(imageView));
}
/**
* 拒绝网络下载
*/
public void denyNetworkDownloads(boolean denyNetworkDownloads) {
engine.denyNetworkDownloads(denyNetworkDownloads);
}
/**
* 处理网络慢
* - otherwise.
*/
public void handleSlowNetwork(boolean handleSlowNetwork) {
engine.handleSlowNetwork(handleSlowNetwork);
}
/**
*/
public void pause() {
engine.pause();
}
/**
*
*/
public void resume() {
engine.resume();
}
/**
*/
public void stop() {
engine.stop();
}
/**
* 销毁
*/
public void destroy() {
stop();
configuration.diskCache.close();
engine = null;
configuration = null;
}
private static Handler defineHandler(DisplayImageOptions options) {
Handler handler = options.getHandler();
if (options.isSyncLoading()) {
handler = null;
} else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) {
handler = new Handler();
}
return handler;
}
/**
* Listener which is designed for synchronous image loading.
*/
private static class SyncImageLoadingListener extends SimpleImageLoadingListener {
private Bitmap loadedImage;
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
this.loadedImage = loadedImage;
}
public Bitmap getLoadedBitmap() {
return loadedImage;
}
}
}
(2)DefaultConfigurationFactory
这个类是一个使用工厂模式方法提供一个队imageloader的参数配置,通过这个类,可以创建一系列的配置参数,通过代码我们可以清楚的看到,它默认使用hashcode的磁盘命名,如果磁盘没有传大小,那么就会使用无限的磁盘缓存策略,如果有传大小的话,那么就使用最近最少使用的磁盘缓存策略,内存缓存的话,如果有传大小,那么使用这个传的大小,如果没有穿参数的话,那么就使用app最大的八分之一,使用最近最少缓存策略,还有就是创建一些图片展示器之类的。
/**
* Factory for providing of default options for {@linkplain ImageLoaderConfiguration configuration}
*一个工厂提供默认的操作对imageloader的参数
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.5.6
*/
public class DefaultConfigurationFactory {
/** 创建一个默认的执行器 */
public static Executor createExecutor(int threadPoolSize, int threadPriority,
QueueProcessingType tasksProcessingType) {
boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
BlockingQueue<Runnable> taskQueue =
lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
createThreadFactory(threadPriority, "uil-pool-"));
}
/** /默认的任务分发Executor */
public static Executor createTaskDistributor() {
return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
}
/**默认的磁盘缓存文件名的生成策略 */
public static FileNameGenerator createFileNameGenerator() {
return new HashCodeFileNameGenerator();
}
/**
* 默认的硬件缓存配置 如果值为0的话就用无限的磁盘缓存 如果大于0就用最近最少使用缓存
*/
public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
long diskCacheSize, int diskCacheFileCount) {
File reserveCacheDir = createReserveDiskCacheDir(context);
if (diskCacheSize > 0 || diskCacheFileCount > 0) {
File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
try {
return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
diskCacheFileCount);
} catch (IOException e) {
L.e(e);
// continue and create unlimited cache
}
}
File cacheDir = StorageUtils.getCacheDirectory(context);
return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
}
/** Creates reserve disk cache folder which will be used if primary disk cache folder becomes unavailable */
private static File createReserveDiskCacheDir(Context context) {//默认的备用硬件缓存目录
File cacheDir = StorageUtils.getCacheDirectory(context, false);
File individualDir = new File(cacheDir, "uil-images");
if (individualDir.exists() || individualDir.mkdir()) {
cacheDir = individualDir;
}
return cacheDir;
}
/**
* Creates default implementation of {@link MemoryCache} - {@link LruMemoryCache}<br />
* Default cache size = 1/8 of available app memory.
*
* 配置 如果传入的默认大小为0 那么就拿到App最大内存的八分之一
* 如果不为0 那么就用传入的大小来作为最近最少使用算法的内存
*/
public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
if (memoryCacheSize == 0) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
if (hasHoneycomb() && isLargeHeap(context)) {
memoryClass = getLargeMemoryClass(am);
}
memoryCacheSize = 1024 * 1024 * memoryClass / 8;
}
return new LruMemoryCache(memoryCacheSize);
}
private static boolean hasHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static boolean isLargeHeap(Context context) {
return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
}
//
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static int getLargeMemoryClass(ActivityManager am) {
return am.getLargeMemoryClass();
}
//创建一个图片下载器
/** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */
public static ImageDownloader createImageDownloader(Context context) {
return new BaseImageDownloader(context);
}
//创建一个图片修改器
/** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */
public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
return new BaseImageDecoder(loggingEnabled);
}
//默认的BitmapDisplayer
/** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
public static BitmapDisplayer createBitmapDisplayer() {
return new SimpleBitmapDisplayer();
}
//默认的TreadFactory
/** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */
private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
return new DefaultThreadFactory(threadPriority, threadNamePrefix);
}
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private final int threadPriority;
DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
this.threadPriority = threadPriority;
group = Thread.currentThread().getThreadGroup();
namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon()) t.setDaemon(false);
t.setPriority(threadPriority);
return t;
}
}
}
(3)DisplayBitmapTask
一个显示图片任务的类,开了一个子线程实现Runnable接口,主要是run的一个判断,注解才是重点
/**
*显示图片任务的一个类
*/
final class DisplayBitmapTask implements Runnable {
private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
private final Bitmap bitmap;
private final String imageUri;//
private final ImageAware imageAware;//
private final String memoryCacheKey;
private final BitmapDisplayer displayer;
private final ImageLoadingListener listener;
private final ImageLoaderEngine engine;
private final LoadedFrom loadedFrom;
//初始化
public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine,
LoadedFrom loadedFrom) {
this.bitmap = bitmap;
imageUri = imageLoadingInfo.uri;
imageAware = imageLoadingInfo.imageAware;
memoryCacheKey = imageLoadingInfo.memoryCacheKey;
displayer = imageLoadingInfo.options.getDisplayer();
listener = imageLoadingInfo.listener;
this.engine = engine;
this.loadedFrom = loadedFrom;
}
@Override
public void run() {
if (imageAware.isCollected()) {//判断图片View是否已经被回收了,如果已经被回收,则取消任务
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else if (isViewWasReused()) {//图片View是否又用来展示其他图片了,如果是的话,则取消任务
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else {//展示图片 完成接口回调
L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
displayer.display(bitmap, imageAware, loadedFrom);
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
}
}
/** 检查imageview是否有效 */
private boolean isViewWasReused() {
String currentCacheKey = engine.getLoadingUriForView(imageAware);
return !memoryCacheKey.equals(currentCacheKey);
}
}
(4)DisplayImageOptions
展示图片的一个参数操作,展示的时候传入参数,展示的图片会根据传入的参数进行展示。
/**
* 展示图片的一个参数操作
*/
public final class DisplayImageOptions {
private final int imageResOnLoading;
private final int imageResForEmptyUri;
private final int imageResOnFail;
private final Drawable imageOnLoading;
private final Drawable imageForEmptyUri;
private final Drawable imageOnFail;
private final boolean resetViewBeforeLoading;
private final boolean cacheInMemory;
private final boolean cacheOnDisk;
private final ImageScaleType imageScaleType;
private final Options decodingOptions;
private final int delayBeforeLoading;
private final boolean considerExifParams;
private final Object extraForDownloader;
private final BitmapProcessor preProcessor;
private final BitmapProcessor postProcessor;
private final BitmapDisplayer displayer;
private final Handler handler;
private final boolean isSyncLoading;
private DisplayImageOptions(Builder builder) {
imageResOnLoading = builder.imageResOnLoading;
imageResForEmptyUri = builder.imageResForEmptyUri;
imageResOnFail = builder.imageResOnFail;
imageOnLoading = builder.imageOnLoading;
imageForEmptyUri = builder.imageForEmptyUri;
imageOnFail = builder.imageOnFail;
resetViewBeforeLoading = builder.resetViewBeforeLoading;
cacheInMemory = builder.cacheInMemory;
cacheOnDisk = builder.cacheOnDisk;
imageScaleType = builder.imageScaleType;
decodingOptions = builder.decodingOptions;
delayBeforeLoading = builder.delayBeforeLoading;
considerExifParams = builder.considerExifParams;
extraForDownloader = builder.extraForDownloader;
preProcessor = builder.preProcessor;
postProcessor = builder.postProcessor;
displayer = builder.displayer;
handler = builder.handler;
isSyncLoading = builder.isSyncLoading;
}
//判断是否在下载的时候需要显示图片
public boolean shouldShowImageOnLoading() {
return imageOnLoading != null || imageResOnLoading != 0;
}
//空uri的时候是否需要显示图片
public boolean shouldShowImageForEmptyUri() {
return imageForEmptyUri != null || imageResForEmptyUri != 0;
}
//是否需要显示图片在下载失败的时候
public boolean shouldShowImageOnFail() {
return imageOnFail != null || imageResOnFail != 0;
}
//是否应该提前处理图片
public boolean shouldPreProcess() {
return preProcessor != null;
}
//是否后面需要处理图片
public boolean shouldPostProcess() {
return postProcessor != null;
}
//是否延迟加载图片
public boolean shouldDelayBeforeLoading() {
return delayBeforeLoading > 0;
}
//拿到图片
public Drawable getImageOnLoading(Resources res) {
return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading;
}
//拿到图片
public Drawable getImageForEmptyUri(Resources res) {
return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri;
}
//拿到图片
public Drawable getImageOnFail(Resources res) {
return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail;
}
//是否重置view
public boolean isResetViewBeforeLoading() {
return resetViewBeforeLoading;
}
//要不要存到内存中
public boolean isCacheInMemory() {
return cacheInMemory;
}
//要不要存到磁盘中
public boolean isCacheOnDisk() {
return cacheOnDisk;
}
//缩放类型
public ImageScaleType getImageScaleType() {
return imageScaleType;
}
//操作类型
public Options getDecodingOptions() {
return decodingOptions;
}
//延迟加载
public int getDelayBeforeLoading() {
return delayBeforeLoading;
}
public boolean isConsiderExifParams() {
return considerExifParams;
}
//拿到额外添加参数
public Object getExtraForDownloader() {
return extraForDownloader;
}
//拿到提前处理
public BitmapProcessor getPreProcessor() {
return preProcessor;
}
//拿到处理后
public BitmapProcessor getPostProcessor() {
return postProcessor;
}
//拿到展示器
public BitmapDisplayer getDisplayer() {
return displayer;
}
public Handler getHandler() {
return handler;
}
boolean isSyncLoading() {
return isSyncLoading;
}
/**
* Builder for {@link DisplayImageOptions}
*/
public static class Builder {
private int imageResOnLoading = 0; //在图片下载期间显示的图片
private int imageResForEmptyUri = 0;//设置图片Uri为空或是错误的时候显示的图片
private int imageResOnFail = 0; //设置图片加载/解码过程中错误时候显示的图片
private Drawable imageOnLoading = null;//在图片下载期间显示的图片
private Drawable imageForEmptyUri = null;//设置图片Uri为空或是错误的时候显示的图片
private Drawable imageOnFail = null;//设置图片加载/解码过程中错误时候显示的图片
private boolean resetViewBeforeLoading = false; //设置图片在下载前是否重置,复位
private boolean cacheInMemory = false;//设置下载的图片是否缓存在内存中
private boolean cacheOnDisk = false; //设置下载的图片是否缓存在SD卡中
private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
private Options decodingOptions = new Options(); //设置图片的解码类型 //设置图片的解码配置
private int delayBeforeLoading = 0;//设置图片下载前的延迟
private boolean considerExifParams = false;//设置图片以如何的编码方式显示
private Object extraForDownloader = null;//设置额外的内容给ImageDownloade
private BitmapProcessor preProcessor = null;//设置图片加入缓存前,对bitmap进行设置
private BitmapProcessor postProcessor = null; //设置显示前的图片,显示后这个图片一直保留在缓存中
private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
private Handler handler = null;
private boolean isSyncLoading = false;
/**
* 设置参数字段参考上面注释
*/
@Deprecated
public Builder showStubImage(int imageRes) {
imageResOnLoading = imageRes;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder showImageOnLoading(int imageRes) {
imageResOnLoading = imageRes;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder showImageOnLoading(Drawable drawable) {
imageOnLoading = drawable;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder showImageForEmptyUri(int imageRes) {
imageResForEmptyUri = imageRes;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder showImageForEmptyUri(Drawable drawable) {
imageForEmptyUri = drawable;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder showImageOnFail(int imageRes) {
imageResOnFail = imageRes;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder showImageOnFail(Drawable drawable) {
imageOnFail = drawable;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder resetViewBeforeLoading() {
resetViewBeforeLoading = true;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) {
this.resetViewBeforeLoading = resetViewBeforeLoading;
return this;
}
/**
* 设置参数字段参考上面注释
*/
@Deprecated
public Builder cacheInMemory() {
cacheInMemory = true;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder cacheInMemory(boolean cacheInMemory) {
this.cacheInMemory = cacheInMemory;
return this;
}
/**
* 设置参数字段参考上面注释
*/
@Deprecated
public Builder cacheOnDisc() {
return cacheOnDisk(true);
}
/**
* 设置参数字段参考上面注释
*/
@Deprecated
public Builder cacheOnDisc(boolean cacheOnDisk) {
return cacheOnDisk(cacheOnDisk);
}
/**
* S设置参数字段参考上面注释
*/
public Builder cacheOnDisk(boolean cacheOnDisk) {
this.cacheOnDisk = cacheOnDisk;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder imageScaleType(ImageScaleType imageScaleType) {
this.imageScaleType = imageScaleType;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder bitmapConfig(Bitmap.Config bitmapConfig) {
if (bitmapConfig == null)
throw new IllegalArgumentException("bitmapConfig can't be null");
decodingOptions.inPreferredConfig = bitmapConfig;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder decodingOptions(Options decodingOptions) {
if (decodingOptions == null)
throw new IllegalArgumentException("decodingOptions can't be null");
this.decodingOptions = decodingOptions;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder delayBeforeLoading(int delayInMillis) {
this.delayBeforeLoading = delayInMillis;
return this;
}
public Builder extraForDownloader(Object extra) {
this.extraForDownloader = extra;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder considerExifParams(boolean considerExifParams) {
this.considerExifParams = considerExifParams;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder preProcessor(BitmapProcessor preProcessor) {
this.preProcessor = preProcessor;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder postProcessor(BitmapProcessor postProcessor) {
this.postProcessor = postProcessor;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder displayer(BitmapDisplayer displayer) {
if (displayer == null) throw new IllegalArgumentException("displayer can't be null");
this.displayer = displayer;
return this;
}
/**
* 设置参数字段参考上面注释
*/
Builder syncLoading(boolean isSyncLoading) {
this.isSyncLoading = isSyncLoading;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public Builder handler(Handler handler) {
this.handler = handler;
return this;
}
/**
* Sets all options equal to incoming options
*/
public Builder cloneFrom(DisplayImageOptions options) {
imageResOnLoading = options.imageResOnLoading;
imageResForEmptyUri = options.imageResForEmptyUri;
imageResOnFail = options.imageResOnFail;
imageOnLoading = options.imageOnLoading;
imageForEmptyUri = options.imageForEmptyUri;
imageOnFail = options.imageOnFail;
resetViewBeforeLoading = options.resetViewBeforeLoading;
cacheInMemory = options.cacheInMemory;
cacheOnDisk = options.cacheOnDisk;
imageScaleType = options.imageScaleType;
decodingOptions = options.decodingOptions;
delayBeforeLoading = options.delayBeforeLoading;
considerExifParams = options.considerExifParams;
extraForDownloader = options.extraForDownloader;
preProcessor = options.preProcessor;
postProcessor = options.postProcessor;
displayer = options.displayer;
handler = options.handler;
isSyncLoading = options.isSyncLoading;
return this;
}
/**
* 设置参数字段参考上面注释
*/
public DisplayImageOptions build() {
return new DisplayImageOptions(this);
}
}
/**
* 创建一个简单的参数类型
*/
public static DisplayImageOptions createSimple() {
return new Builder().build();
}
}
(5)ImageLoaderConfiguration
这个类我们也是比较熟悉的,因为要使用ImageLoader的时候,我们第一个就是要配置这个参数,我们先来看一下常用的配置手法如下
public static void initImageLoader(Context context) {
File cacheDir = StorageUtils.getOwnCacheDirectory(context,
"bee_k77/Cache");// 获取到缓存的目录地址
Log.e("cacheDir", cacheDir.getPath());
// 创建配置ImageLoader(所有的选项都是可选的,只使用那些你真的想定制),这个可以设定在APPLACATION里面,设置为全局的配置参数
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
context)
// max width, max height,即保存的每个缓存文件的最大长宽
.memoryCacheExtraOptions(480, 800)
// Can slow ImageLoader, use it carefully (Better don't use it)设置缓存的详细信息,最好不要设置这个
/ .discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null)
// 线程池内加载的数量
.threadPoolSize(3)
// 线程优先级
.threadPriority(Thread.NORM_PRIORITY - 2)
/*
* When you display an image in a small ImageView
* and later you try to display this image (from identical URI) in a larger ImageView
* so decoded image of bigger size will be cached in memory as a previous decoded image of smaller size.
* So the default behavior is to allow to cache multiple sizes of one image in memory.
* You can deny it by calling this method:
* so when some image will be cached in memory then previous cached size of this image (if it exists)
* will be removed from memory cache before.
*/
/ .denyCacheImageMultipleSizesInMemory()
// You can pass your own memory cache implementation你可以通过自己的内存缓存实现
// .memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024))
// .memoryCacheSize(2 * 1024 * 1024)
//硬盘缓存50MB
.diskCacheSize(50 * 1024 * 1024)
//将保存的时候的URI名称用MD5
.diskCacheFileNameGenerator(new Md5FileNameGenerator())
// 加密
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator())//将保存的时候的URI名称用HASHCODE加密
.tasksProcessingOrder(QueueProcessingType.LIFO)
.diskCacheFileCount(100) //缓存的File数量
.diskCache(new UnlimitedDiscCache(cacheDir))// 自定义缓存路径
// .defaultDisplayImageOptions(DisplayImageOptions.createSimple())
// .imageDownloader(new BaseImageDownloader(context, 5 * 1000,
// 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)超时时间
.writeDebugLogs() // Remove for release app
.build();
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config);// 全局初始化此配置
}
我们可以清楚看到建造者模式和链式调用的身影,接下来我们来看一下源码的实现。
public final class ImageLoaderConfiguration {
final Resources resources;
final int maxImageWidthForMemoryCache;//
final int maxImageHeightForMemoryCache;
final int maxImageWidthForDiskCache;//最大图片
final int maxImageHeightForDiskCache;
final BitmapProcessor processorForDiskCache;
final Executor taskExecutor;
final Executor taskExecutorForCachedImages;
final boolean customExecutor;
final boolean customExecutorForCachedImages;
final int threadPoolSize;
final int threadPriority;
final QueueProcessingType tasksProcessingType;
final MemoryCache memoryCache;
final DiskCache diskCache;
final ImageDownloader downloader;
final ImageDecoder decoder;
final DisplayImageOptions defaultDisplayImageOptions;
final ImageDownloader networkDeniedDownloader;
final ImageDownloader slowNetworkDownloader;
private ImageLoaderConfiguration(final Builder builder) {
resources = builder.context.getResources();
maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;
maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;
processorForDiskCache = builder.processorForDiskCache;
taskExecutor = builder.taskExecutor;
taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
threadPoolSize = builder.threadPoolSize;
threadPriority = builder.threadPriority;
tasksProcessingType = builder.tasksProcessingType;
diskCache = builder.diskCache;
memoryCache = builder.memoryCache;
defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
downloader = builder.downloader;
decoder = builder.decoder;
customExecutor = builder.customExecutor;
customExecutorForCachedImages = builder.customExecutorForCachedImages;
networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader);
slowNetworkDownloader = new SlowNetworkImageDownloader(downloader);
}
/**
* 初始化一个空
*/
public static ImageLoaderConfiguration createDefault(Context context) {
return new Builder(context).build();
}
ImageSize getMaxImageSize() {
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
int width = maxImageWidthForMemoryCache;
if (width <= 0) {
width = displayMetrics.widthPixels;
}
int height = maxImageHeightForMemoryCache;
if (height <= 0) {
height = displayMetrics.heightPixels;
}
return new ImageSize(width, height);
}
/**
* Builder for {@link ImageLoaderConfiguration}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public static class Builder {
private static final String WARNING_OVERLAP_DISK_CACHE_PARAMS = "diskCache(), diskCacheSize() and diskCacheFileCount calls overlap each other";
private static final String WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR = "diskCache() and diskCacheFileNameGenerator() 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.";
/**
* {@value}
*/
public static final int DEFAULT_THREAD_POOL_SIZE = 3;
/**
* {@value}
*/
public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 2;
/**
* {@value}
*/
public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO;
private Context context;
private int maxImageWidthForMemoryCache = 0;
private int maxImageHeightForMemoryCache = 0;
private int maxImageWidthForDiskCache = 0;
private int maxImageHeightForDiskCache = 0;
private BitmapProcessor processorForDiskCache = null;
private Executor taskExecutor = null;
private Executor taskExecutorForCachedImages = null;
private boolean customExecutor = false;
private boolean customExecutorForCachedImages = false;
private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
private int threadPriority = DEFAULT_THREAD_PRIORITY;
private boolean denyCacheImageMultipleSizesInMemory = false;
private QueueProcessingType tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE;
private int memoryCacheSize = 0;
private long diskCacheSize = 0;
private int diskCacheFileCount = 0;
private MemoryCache memoryCache = null;
private DiskCache diskCache = null;
private FileNameGenerator diskCacheFileNameGenerator = null;
private ImageDownloader downloader = null;
private ImageDecoder decoder;
private DisplayImageOptions defaultDisplayImageOptions = null;
private boolean writeLogs = false;
public Builder(Context context) {
this.context = context.getApplicationContext();
}
/**即保存的每个缓存文件的最大长宽
*/
public Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) {
this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache;
this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache;
return this;
}
/**
*/
@Deprecated
public Builder discCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
BitmapProcessor processorForDiskCache) {
return diskCacheExtraOptions(maxImageWidthForDiskCache, maxImageHeightForDiskCache, processorForDiskCache);
}
/**
*/
public Builder diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
BitmapProcessor processorForDiskCache) {
this.maxImageWidthForDiskCache = maxImageWidthForDiskCache;
this.maxImageHeightForDiskCache = maxImageHeightForDiskCache;
this.processorForDiskCache = processorForDiskCache;
return this;
}
/**
*/
public Builder taskExecutor(Executor executor) {
if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
}
this.taskExecutor = executor;
return this;
}
/**
*/
public Builder taskExecutorForCachedImages(Executor executorForCachedImages) {
if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
}
this.taskExecutorForCachedImages = executorForCachedImages;
return this;
}
/**
* // 线程池内加载的数量
*/
public Builder threadPoolSize(int threadPoolSize) {
if (taskExecutor != null || taskExecutorForCachedImages != null) {
}
this.threadPoolSize = threadPoolSize;
return this;
}
/**
// 线程优先级
*/
public Builder threadPriority(int threadPriority) {
if (taskExecutor != null || taskExecutorForCachedImages != null) {
}
if (threadPriority < Thread.MIN_PRIORITY) {
this.threadPriority = Thread.MIN_PRIORITY;
} else {
if (threadPriority > Thread.MAX_PRIORITY) {
this.threadPriority = Thread.MAX_PRIORITY;
} else {
this.threadPriority = threadPriority;
}
}
return this;
}
/**
*/
public Builder denyCacheImageMultipleSizesInMemory() {
this.denyCacheImageMultipleSizesInMemory = true;
return this;
}
/**
* 设置处理队列类型
* Default value - {@link QueueProcessingType#FIFO}
*/
public Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) {
if (taskExecutor != null || taskExecutorForCachedImages != null) {
}
this.tasksProcessingType = tasksProcessingType;
return this;
}
/**
*/
public Builder memoryCacheSize(int memoryCacheSize) {
if (memoryCacheSize <= 0)
throw new IllegalArgumentException("memoryCacheSize must be a positive number");
if (memoryCache != null) {
}
this.memoryCacheSize = memoryCacheSize;
return this;
}
/**
* Sets maximum memory cache size (in percent of available app memory) for {@link android.graphics.Bitmap
* bitmaps}.<br />
* Default value - 1/8 of available app memory.<br />
* <b>NOTE:</b> If you use this method then
* memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
* {@link MemoryCache}.
*/
public Builder memoryCacheSizePercentage(int availableMemoryPercent) {
if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) {
throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)");
}
if (memoryCache != null) {
}
long availableMemory = Runtime.getRuntime().maxMemory();
memoryCacheSize = (int) (availableMemory * (availableMemoryPercent / 100f));
return this;
}
/**
* Sets memory cache for {@link android.graphics.Bitmap bitmaps}.<br />
* with limited memory cache size (size = 1/8 of available app memory)<br />
* <br />
* <b>NOTE:</b> If you set custom memory cache then following configuration option will not be considered:
* <ul>
* <li>{@link #memoryCacheSize(int)}</li>
* </ul>
*/
public Builder memoryCache(MemoryCache memoryCache) {
if (memoryCacheSize != 0) {
}
this.memoryCache = memoryCache;
return this;
}
/**
//硬盘缓存50MB
*/
@Deprecated
public Builder discCacheSize(int maxCacheSize) {
return diskCacheSize(maxCacheSize);
}
/**
*/
public Builder diskCacheSize(int maxCacheSize) {
if (maxCacheSize <= 0)
throw new IllegalArgumentException("maxCacheSize must be a positive number");
if (diskCache != null) {
}
this.diskCacheSize = maxCacheSize;
return this;
}
/**
* @deprecated Use {@link #diskCacheFileCount(int)} instead
*/
@Deprecated
public Builder discCacheFileCount(int maxFileCount) {
return diskCacheFileCount(maxFileCount);
}
/**
* Sets maximum file count in disk cache directory.<br />
* By default: disk cache is unlimited.<br />
* <b>NOTE:</b> If you use this method then
* will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
* implementation of {@link DiskCache}
*/
public Builder diskCacheFileCount(int maxFileCount) {
if (maxFileCount <= 0)
throw new IllegalArgumentException("maxFileCount must be a positive number");
if (diskCache != null) {
}
this.diskCacheFileCount = maxFileCount;
return this;
}
/** //将保存的时候的URI名称用MD5
*/
@Deprecated
public Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
return diskCacheFileNameGenerator(fileNameGenerator);
}
/**
* Sets name generator for files cached in disk cache.<br />
* Default value -
* DefaultConfigurationFactory.createFileNameGenerator()}
*/
public Builder diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
if (diskCache != null) {
}
this.diskCacheFileNameGenerator = fileNameGenerator;
return this;
}
/**
*/
@Deprecated
public Builder discCache(DiskCache diskCache) {
return diskCache(diskCache);
}
/**
设置磁盘缓存策略
*/
public Builder diskCache(DiskCache diskCache) {
if (diskCacheSize > 0 || diskCacheFileCount > 0) {
}
if (diskCacheFileNameGenerator != null) {
}
this.diskCache = diskCache;
return this;
}
/**
设置图片下载器
*/
public Builder imageDownloader(ImageDownloader imageDownloader) {
this.downloader = imageDownloader;
return this;
}
/**
设置图片展示器
*/
public Builder imageDecoder(ImageDecoder imageDecoder) {
this.decoder = imageDecoder;
return this;
}
/**
设置默认的图片展示操作参数
*/
public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {
this.defaultDisplayImageOptions = defaultDisplayImageOptions;
return this;
}
/**是否写日志
*/
public Builder writeDebugLogs() {
this.writeLogs = true;
return this;
}
/**
* 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();
}
}
}
/**
网络图片下载器
*/
private static class NetworkDeniedImageDownloader implements ImageDownloader {
private final ImageDownloader wrappedDownloader;
public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
this.wrappedDownloader = wrappedDownloader;
}
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
throw new IllegalStateException();
default:
return wrappedDownloader.getStream(imageUri, extra);
}
}
}
/**
* 网络慢的时候使用一个流刷
*/
private static class SlowNetworkImageDownloader implements ImageDownloader {
private final ImageDownloader wrappedDownloader;
public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
this.wrappedDownloader = wrappedDownloader;
}
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return new FlushedInputStream(imageStream);
default:
return imageStream;
}
}
}
}
(6)ImageLoaderEngine
这是一个负责将图片加载任务分发到各个线程执行的类
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package leakcanary.imageloader.core;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import leakcanary.imageloader.core.imageaware.ImageAware;
/**
*负责将图片加载任务分发到各个线程执行。
*/
class ImageLoaderEngine {
final ImageLoaderConfiguration configuration;//配置信息
private Executor taskExecutor;//执行从源获取图片任务的executor
private Executor taskExecutorForCachedImages;//执行从缓存获取图片任务的executor
private Executor taskDistributor;//任务分发池
private final Map<Integer, String> cacheKeysForImageAwares = Collections
.synchronizedMap(new HashMap<Integer, String>());
private final Map<String, ReentrantLock> uriLocks = new WeakHashMap<String, ReentrantLock>();
private final AtomicBoolean paused = new AtomicBoolean(false);//是否暂停执行任务
private final AtomicBoolean networkDenied = new AtomicBoolean(false);//是否拒绝网络访问
private final AtomicBoolean slowNetwork = new AtomicBoolean(false);//是否是慢网络情况
private final Object pauseLock = new Object();//暂停等待锁
//初始化
ImageLoaderEngine(ImageLoaderConfiguration configuration) {
this.configuration = configuration;
taskExecutor = configuration.taskExecutor;
taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;
taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
}
//提交LoadAndDisplayImageTask任务
void submit(final LoadAndDisplayImageTask task) {
taskDistributor.execute(new Runnable() {
@Override
public void run() {
File image = configuration.diskCache.get(task.getLoadingUri());
boolean isImageCachedOnDisk = image != null && image.exists();
initExecutorsIfNeed();
if (isImageCachedOnDisk) {
taskExecutorForCachedImages.execute(task);
} else {
taskExecutor.execute(task);
}
}
});
}
/** 直接交由taskExecutorForCachedImages执行。 */
void submit(ProcessAndDisplayImageTask task) {
initExecutorsIfNeed();
taskExecutorForCachedImages.execute(task);
}
private void initExecutorsIfNeed() {
if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) {
taskExecutor = createTaskExecutor();
}
if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages)
.isShutdown()) {
taskExecutorForCachedImages = createTaskExecutor();
}
}
private Executor createTaskExecutor() {
return DefaultConfigurationFactory
.createExecutor(configuration.threadPoolSize, configuration.threadPriority,
configuration.tasksProcessingType);
}
/**
*/
String getLoadingUriForView(ImageAware imageAware) {
return cacheKeysForImageAwares.get(imageAware.getId());
}
/**
* Associates <b>memoryCacheKey</b> with <b>imageAware</b>. Then it helps to define image URI is loaded into View at
* exact moment.
*/
void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) {
cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey);
}
/**
* Cancels the task of loading and displaying image for incoming <b>imageAware</b>.
*
* will be cancelled
*/
void cancelDisplayTaskFor(ImageAware imageAware) {
cacheKeysForImageAwares.remove(imageAware.getId());
}
/**
* Denies or allows engine to download images from the network.<br /> <br /> If downloads are denied and if image
*
* @param denyNetworkDownloads pass <b>true</b> - to deny engine to download images from the network; <b>false</b> -
* to allow engine to download images from network.
*/
void denyNetworkDownloads(boolean denyNetworkDownloads) {
networkDenied.set(denyNetworkDownloads);
}
/**
* href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not.
*
* - otherwise.
*/
void handleSlowNetwork(boolean handleSlowNetwork) {
slowNetwork.set(handleSlowNetwork);
}
/**
* Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.<br
* /> Already running tasks are not paused.
*/
void pause() {
paused.set(true);
}
/** Resumes engine work. Paused "load&display" tasks will continue its work. */
void resume() {
paused.set(false);
synchronized (pauseLock) {
pauseLock.notifyAll();
}
}
/**
* Stops engine, cancels all running and scheduled display image tasks. Clears internal data.
* <br />
* <b>NOTE:</b> This method doesn't shutdown
* custom task executors} if you set them.
*/
void stop() {
if (!configuration.customExecutor) {
((ExecutorService) taskExecutor).shutdownNow();
}
if (!configuration.customExecutorForCachedImages) {
((ExecutorService) taskExecutorForCachedImages).shutdownNow();
}
cacheKeysForImageAwares.clear();
uriLocks.clear();
}
void fireCallback(Runnable r) {
taskDistributor.execute(r);
}
ReentrantLock getLockForUri(String uri) {
ReentrantLock lock = uriLocks.get(uri);
if (lock == null) {
lock = new ReentrantLock();
uriLocks.put(uri, lock);
}
return lock;
}
AtomicBoolean getPause() {
return paused;
}
Object getPauseLock() {
return pauseLock;
}
boolean isNetworkDenied() {
return networkDenied.get();
}
boolean isSlowNetwork() {
return slowNetwork.get();
}
}
(7)ImageLoadingInfo 一个展示和下载的实体类包含多种内容
final class ImageLoadingInfo {
final String uri;//地址
final String memoryCacheKey;//内存缓存的键
final ImageAware imageAware;//imageview
final ImageSize targetSize;//目标大小
final DisplayImageOptions options;//图片操作
final ImageLoadingListener listener;//图片下载监听器
final ImageLoadingProgressListener progressListener;//图片处理监听器
final ReentrantLock loadFromUriLock;//锁
//构造方法
public ImageLoadingInfo(String uri, ImageAware imageAware, ImageSize targetSize, String memoryCacheKey,
DisplayImageOptions options, ImageLoadingListener listener,
ImageLoadingProgressListener progressListener, ReentrantLock loadFromUriLock) {
this.uri = uri;
this.imageAware = imageAware;
this.targetSize = targetSize;
this.options = options;
this.listener = listener;
this.progressListener = progressListener;
this.loadFromUriLock = loadFromUriLock;
this.memoryCacheKey = memoryCacheKey;
}
}
(8)ProcessAndDisplayImageTask
对象交给ProcessAndDisplayImageTask,见名知意,这个对象就是使ImageView显示图片的,是一个Ruannable。
final class ProcessAndDisplayImageTask implements Runnable {
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
private final ImageLoaderEngine engine;
private final Bitmap bitmap;
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo,
Handler handler) {
this.engine = engine;
this.bitmap = bitmap;
this.imageLoadingInfo = imageLoadingInfo;
this.handler = handler;
}
@Override
public void run() {
BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
Bitmap processedBitmap = processor.process(bitmap);
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
LoadedFrom.MEMORY_CACHE);
LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
}
}
/**对象交给ProcessAndDisplayImageTask,见名知意,这个对象就是使ImageView显示图片的,是一个Ruannable。
*/
final class ProcessAndDisplayImageTask implements Runnable {
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
private final ImageLoaderEngine engine;
private final Bitmap bitmap;
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo,
Handler handler) {
this.engine = engine;
this.bitmap = bitmap;
this.imageLoadingInfo = imageLoadingInfo;
this.handler = handler;
}
@Override
public void run() {
//获取DisplayImageOptions配置的BitmapProcessor 对象
BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
// 执行BitmapProcessor 的process方法对Bitmap进行加工处理
Bitmap processedBitmap = processor.process(bitmap);
// 将加工过的Bitmap传给DisplayBitmapTask对象,该对象也是一个Runnable。
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
LoadedFrom.MEMORY_CACHE);
//真正展示的方法
LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
}
}
(9)LoadAndDisplayImageTask
这个是一个图片下载展示的类,继承runnable,是为了三级缓存用
final class LoadAndDisplayImageTask implements Runnable, IoUtils.CopyListener {
private static final String LOG_WAITING_FOR_RESUME = "ImageLoader is paused. Waiting... [%s]";
private static final String LOG_RESUME_AFTER_PAUSE = ".. Resume loading [%s]";
private static final String LOG_DELAY_BEFORE_LOADING = "Delay %d ms before loading... [%s]";
private static final String LOG_START_DISPLAY_IMAGE_TASK = "Start display image task [%s]";
private static final String LOG_WAITING_FOR_IMAGE_LOADED = "Image already is loading. Waiting... [%s]";
private static final String LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING = "...Get cached bitmap from memory after waiting. [%s]";
private static final String LOG_LOAD_IMAGE_FROM_NETWORK = "Load image from network [%s]";
private static final String LOG_LOAD_IMAGE_FROM_DISK_CACHE = "Load image from disk cache [%s]";
private static final String LOG_RESIZE_CACHED_IMAGE_FILE = "Resize image in disk cache [%s]";
private static final String LOG_PREPROCESS_IMAGE = "PreProcess image before caching in memory [%s]";
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
private static final String LOG_CACHE_IMAGE_IN_MEMORY = "Cache image in memory [%s]";
private static final String LOG_CACHE_IMAGE_ON_DISK = "Cache image on disk [%s]";
private static final String LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK = "Process image before cache on disk [%s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
private static final String LOG_TASK_INTERRUPTED = "Task was interrupted [%s]";
private static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]";
private static final String ERROR_PRE_PROCESSOR_NULL = "Pre-processor returned null [%s]";
private static final String ERROR_POST_PROCESSOR_NULL = "Post-processor returned null [%s]";
private static final String ERROR_PROCESSOR_FOR_DISK_CACHE_NULL = "Bitmap processor for disk cache returned null [%s]";
private final ImageLoaderEngine engine;
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
// Helper references
private final ImageLoaderConfiguration configuration;
private final ImageDownloader downloader;
private final ImageDownloader networkDeniedDownloader;
private final ImageDownloader slowNetworkDownloader;
private final ImageDecoder decoder;
final String uri;
private final String memoryCacheKey;
final ImageAware imageAware;
private final ImageSize targetSize;
final DisplayImageOptions options;
final ImageLoadingListener listener;
final ImageLoadingProgressListener progressListener;
private final boolean syncLoading;
// State vars
private LoadedFrom loadedFrom = LoadedFrom.NETWORK;
//初始化
public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) {
this.engine = engine;
this.imageLoadingInfo = imageLoadingInfo;
this.handler = handler;
configuration = engine.configuration;
downloader = configuration.downloader;
networkDeniedDownloader = configuration.networkDeniedDownloader;
slowNetworkDownloader = configuration.slowNetworkDownloader;
decoder = configuration.decoder;
uri = imageLoadingInfo.uri;
memoryCacheKey = imageLoadingInfo.memoryCacheKey;
imageAware = imageLoadingInfo.imageAware;
targetSize = imageLoadingInfo.targetSize;
options = imageLoadingInfo.options;
listener = imageLoadingInfo.listener;
progressListener = imageLoadingInfo.progressListener;
syncLoading = options.isSyncLoading();
}
//跑起来
@Override
public void run() {
if (waitIfPaused()) return;//如果需要等待
if (delayIfNeed()) return;//如果需要延迟
//加一个锁
ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
if (loadFromUriLock.isLocked()) {
L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
}
loadFromUriLock.lock();
Bitmap bmp;
try {
checkTaskNotActual();
//从内存中获取
bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {//如果没有内存
bmp = tryLoadBitmap();//这个方法尝试从磁盘中拿到,磁盘没就从网络拿
if (bmp == null) return; // listener callback already was fired
checkTaskNotActual();
checkTaskInterrupted();
if (options.shouldPreProcess()) { //是否应该预处理
L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
bmp = options.getPreProcessor().process(bmp);//处理
if (bmp == null) {
L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
}
}
if (bmp != null && options.isCacheInMemory()) {//如果从内存拿的 放回去
L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
configuration.memoryCache.put(memoryCacheKey, bmp);
}
} else {
loadedFrom = LoadedFrom.MEMORY_CACHE;
L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
}
if (bmp != null && options.shouldPostProcess()) {//如果拿到了就处理
L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
bmp = options.getPostProcessor().process(bmp);
if (bmp == null) {
L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
}
}
checkTaskNotActual();
checkTaskInterrupted();
} catch (TaskCancelledException e) {
fireCancelEvent();
return;
} finally {
loadFromUriLock.unlock();//关闭锁
}
//展示
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);
}
/**
* 是否应该等待
*/
private boolean waitIfPaused() {
AtomicBoolean pause = engine.getPause();
if (pause.get()) {
synchronized (engine.getPauseLock()) {
if (pause.get()) {
L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
try {
engine.getPauseLock().wait();
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
}
}
}
return isTaskNotActual();
}
/**
* 收应该延迟
*/
private boolean delayIfNeed() {
if (options.shouldDelayBeforeLoading()) {
L.d(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey);
try {
Thread.sleep(options.getDelayBeforeLoading());
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
return isTaskNotActual();
}
return false;
}
//尝试从磁盘拿到缓存,没有就请求网络
private Bitmap tryLoadBitmap() throws TaskCancelledException {
Bitmap bitmap = null;
try {
File imageFile = configuration.diskCache.get(uri);//拿到文件
if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {//文件不为空
L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
loadedFrom = LoadedFrom.DISC_CACHE;//标识来源
checkTaskNotActual();
bitmap = decodeImage(FILE.wrap(imageFile.getAbsolutePath()));//修饰图片
}
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {//如果磁盘没有
L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
loadedFrom = LoadedFrom.NETWORK;//标识网络来源
String imageUriForDecoding = uri;
if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
imageFile = configuration.diskCache.get(uri);
if (imageFile != null) {
imageUriForDecoding = FILE.wrap(imageFile.getAbsolutePath());
}
}
//从网络中拿到图片
checkTaskNotActual();
bitmap = decodeImage(imageUriForDecoding);
//如果没那就失败
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
fireFailEvent(FailType.DECODING_ERROR, null);
}
}
} catch (IllegalStateException e) {
fireFailEvent(FailType.NETWORK_DENIED, null);
} catch (TaskCancelledException e) {
throw e;
} catch (IOException e) {
L.e(e);
fireFailEvent(FailType.IO_ERROR, e);
} catch (OutOfMemoryError e) {
L.e(e);
fireFailEvent(FailType.OUT_OF_MEMORY, e);
} catch (Throwable e) {
L.e(e);
fireFailEvent(FailType.UNKNOWN, e);
}
return bitmap;
}
//修饰
private Bitmap decodeImage(String imageUri) throws IOException {
ViewScaleType viewScaleType = imageAware.getScaleType();
ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
getDownloader(), options);
return decoder.decode(decodingInfo);
}
/**
* 尝试保存图片到缓存
*/
private boolean tryCacheImageOnDisk() throws TaskCancelledException {
L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);
boolean loaded;
try {
loaded = downloadImage();
if (loaded) {
int width = configuration.maxImageWidthForDiskCache;
int height = configuration.maxImageHeightForDiskCache;
if (width > 0 || height > 0) {
L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey);
resizeAndSaveImage(width, height); // TODO : process boolean result
}
}
} catch (IOException e) {
L.e(e);
loaded = false;
}
return loaded;
}
//下载图片
private boolean downloadImage() throws IOException {
InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
if (is == null) {
L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
return false;
} else {
try {
return configuration.diskCache.save(uri, is, this);
} finally {
IoUtils.closeSilently(is);
}
}
}
/**
* 压缩图片保存图片
*/
private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException {
// Decode image file, compress and re-save it
boolean saved = false;
File targetFile = configuration.diskCache.get(uri);
if (targetFile != null && targetFile.exists()) {
ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight);
DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();
ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey,
FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE,
getDownloader(), specialOptions);
Bitmap bmp = decoder.decode(decodingInfo);
if (bmp != null && configuration.processorForDiskCache != null) {
L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey);
bmp = configuration.processorForDiskCache.process(bmp);
if (bmp == null) {
L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey);
}
}
if (bmp != null) {
saved = configuration.diskCache.save(uri, bmp);
bmp.recycle();
}
}
return saved;
}
@Override
public boolean onBytesCopied(int current, int total) {
return syncLoading || fireProgressEvent(current, total);
}
/**
* @return <b>true</b> - if loading should be continued; <b>false</b> - if loading should be interrupted
*/
private boolean fireProgressEvent(final int current, final int total) {
if (isTaskInterrupted() || isTaskNotActual()) return false;
if (progressListener != null) {
Runnable r = new Runnable() {
@Override
public void run() {
progressListener.onProgressUpdate(uri, imageAware.getWrappedView(), current, total);
}
};
runTask(r, false, handler, engine);
}
return true;
}
private void fireFailEvent(final FailType failType, final Throwable failCause) {
if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return;
Runnable r = new Runnable() {
@Override
public void run() {
if (options.shouldShowImageOnFail()) {
imageAware.setImageDrawable(options.getImageOnFail(configuration.resources));
}
listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause));
}
};
runTask(r, false, handler, engine);
}
private void fireCancelEvent() {
if (syncLoading || isTaskInterrupted()) return;
Runnable r = new Runnable() {
@Override
public void run() {
listener.onLoadingCancelled(uri, imageAware.getWrappedView());
}
};
runTask(r, false, handler, engine);
}
//拿到下载器
private ImageDownloader getDownloader() {
ImageDownloader d;
if (engine.isNetworkDenied()) {
d = networkDeniedDownloader;
} else if (engine.isSlowNetwork()) {
d = slowNetworkDownloader;
} else {
d = downloader;
}
return d;
}
/**判断是否被回收
*/
private void checkTaskNotActual() throws TaskCancelledException {
checkViewCollected();
checkViewReused();
}
/**判断view是否被回收
*/
private boolean isTaskNotActual() {
return isViewCollected() || isViewReused();
}
/**
* @throws TaskCancelledException if target ImageAware is collected
*/
private void checkViewCollected() throws TaskCancelledException {
if (isViewCollected()) {
throw new TaskCancelledException();
}
}
/**
* ImageAare是否被回收
*/
private boolean isViewCollected() {
if (imageAware.isCollected()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
return true;
}
return false;
}
/**
* ImageAware。是否被回收
*/
private void checkViewReused() throws TaskCancelledException {
if (isViewReused()) {
throw new TaskCancelledException();
}
}
/**
* imageAware是否被展示其他图片了
*/
private boolean isViewReused() {
String currentCacheKey = engine.getLoadingUriForView(imageAware);
// Check whether memory cache key (image URI) for current ImageAware is actual.
// If ImageAware is reused for another task then current task should be cancelled.
boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey);
if (imageAwareWasReused) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
return true;
}
return false;
}
/**
* 检查任务是否被中断
*/
private void checkTaskInterrupted() throws TaskCancelledException {
if (isTaskInterrupted()) {
throw new TaskCancelledException();
}
}
/**
* 检查任务是否被中断
*/
private boolean isTaskInterrupted() {
if (Thread.interrupted()) {
L.d(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
return false;
}
//拿到uri
String getLoadingUri() {
return uri;
}
//运行任务
static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
if (sync) {
r.run();
} else if (handler == null) {
engine.fireCallback(r);
} else {
handler.post(r);
}
}
//异常类
class TaskCancelledException extends Exception {
}
}
三、其他辅助类讲解
好了讲完核心类,我们来看一下辅助类们的结构
(1)ImageLoadingListener,
我们先看一下监听相关的类,下面ImageLoadingListener定义了图片加载监听的接口,包括开始,下载失败,加载完成,加载取消,后面还有ImageLoadingProgressListener,这个监听是带有进度条的监听,下面还有接口的简单实现类SimpleImageLoadingListener,你可以自定义监听,实现上面的接口就可以了
/**
* 图片加载的监听器接口
*/
public interface ImageLoadingListener {
/**
* 开始下载
*/
void onLoadingStarted(String imageUri, View view);
/**
* 下载失败
*/
void onLoadingFailed(String imageUri, View view, FailReason failReason);
/**
*加载完成
*/
void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);
/**加载取消
*/
void onLoadingCancelled(String imageUri, View view);
}
public interface ImageLoadingProgressListener {
void onProgressUpdate(String imageUri, View view, int current, int total);
}
public class SimpleImageLoadingListener implements ImageLoadingListener {
@Override
public void onLoadingStarted(String imageUri, View view) {
// Empty implementation
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
// Empty implementation
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Empty implementation
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
// Empty implementation
}
}
(2)接下来,我们看一下ImageAware这包,我们先看一下ImageAware这个接口定义
/**
ImageAware的接口
*/
public interface ImageAware {
/**拿到宽度
*/
int getWidth();
/**拿到高度
*/
int getHeight();
/**拿到缩放的类型
*/
ViewScaleType getScaleType();
/**
*/
View getWrappedView();
/**
*/
boolean isCollected();
/**
*/
int getId();
/**
*/
boolean setImageDrawable(Drawable drawable);
/**
*/
boolean setImageBitmap(Bitmap bitmap);
}
然后我们看实现这个接口的抽象类ViewAware,主要是暴露两个抽象方法让子类去设置图片,还有就是提供一些获取和保存属性的方法
public abstract class ViewAware implements ImageAware {
public static final String WARN_CANT_SET_DRAWABLE = "Can't set a drawable into view. You should call ImageLoader on UI thread for it.";
public static final String WARN_CANT_SET_BITMAP = "Can't set a bitmap into view. You should call ImageLoader on UI thread for it.";
protected Reference<View> viewRef;
protected boolean checkActualViewSize;
public ViewAware(View view) {
this(view, true);
}
public ViewAware(View view, boolean checkActualViewSize) {
if (view == null) throw new IllegalArgumentException("view must not be null");
this.viewRef = new WeakReference<View>(view);//将传入的的View搞成弱引用
this.checkActualViewSize = checkActualViewSize;//检查实际view大小
}
/**拿到View的宽
*/
@Override
public int getWidth() {
View view = viewRef.get();
if (view != null) {
final ViewGroup.LayoutParams params = view.getLayoutParams();
int width = 0;
if (checkActualViewSize && params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
width = view.getWidth(); // Get actual image width
}
if (width <= 0 && params != null) width = params.width; // Get layout width parameter
return width;
}
return 0;
}
/**
* 拿到高度
* @return
*/
@Override
public int getHeight() {
View view = viewRef.get();
if (view != null) {
final ViewGroup.LayoutParams params = view.getLayoutParams();
int height = 0;
if (checkActualViewSize && params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
height = view.getHeight(); // Get actual image height
}
if (height <= 0 && params != null)
height = params.height; // Get layout height parameter
return height;
}
return 0;
}
//拿到缩放类型
@Override
public ViewScaleType getScaleType() {
return ViewScaleType.CROP;
}
//拿到传入的View
@Override
public View getWrappedView() {
return viewRef.get();
}
//是否被回收
@Override
public boolean isCollected() {
return viewRef.get() == null;
}
//拿到view的hashcode
@Override
public int getId() {
View view = viewRef.get();
return view == null ? super.hashCode() : view.hashCode();
}
//设置drawable
@Override
public boolean setImageDrawable(Drawable drawable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
View view = viewRef.get();
if (view != null) {
setImageDrawableInto(drawable, view);
return true;
}
} else {
}
return false;
}
//设置一个bitmap
@Override
public boolean setImageBitmap(Bitmap bitmap) {
if (Looper.myLooper() == Looper.getMainLooper()) {
View view = viewRef.get();
if (view != null) {
setImageBitmapInto(bitmap, view);
return true;
}
} else {
}
return false;
}
/**
*抽象类在子类中设置图片
*/
protected abstract void setImageDrawableInto(Drawable drawable, View view);
/**
*设置Bitmap
*/
protected abstract void setImageBitmapInto(Bitmap bitmap, View view);
}
接下来我们看一一下这个抽象类的实现,其实就是实现父类的抽象方法,实现图片的设置
* * ImageViewAware主要是做什么的呢?
* 该类主要是将ImageView进行一个包装,
* 将ImageView的强引用变成弱引用,当内存不足的时候,
* 可以更好的回收ImageView对象,还有就是获取ImageView的宽度和高度。
* 这使得我们可以根据ImageView的宽高去对图片进行一个裁剪,减少内存的使用。
*/
public class ImageViewAware extends ViewAware {
public ImageViewAware(ImageView imageView) {
super(imageView);
}
public ImageViewAware(ImageView imageView, boolean checkActualViewSize) {
super(imageView, checkActualViewSize);
}
/**
* {@inheritDoc}
* <br />
* 3) Get <b>maxWidth</b>.
*/
@Override
public int getWidth() {
int width = super.getWidth();
if (width <= 0) {
ImageView imageView = (ImageView) viewRef.get();
if (imageView != null) {
width = getImageViewFieldValue(imageView, "mMaxWidth"); // Check maxWidth parameter
}
}
return width;
}
/**
* {@inheritDoc}
* <br />
* 3) Get <b>maxHeight</b>
*/
@Override
public int getHeight() {
int height = super.getHeight();
if (height <= 0) {
ImageView imageView = (ImageView) viewRef.get();
if (imageView != null) {
height = getImageViewFieldValue(imageView, "mMaxHeight"); // Check maxHeight parameter
}
}
return height;
}
@Override
public ViewScaleType getScaleType() {
ImageView imageView = (ImageView) viewRef.get();
if (imageView != null) {
return ViewScaleType.fromImageView(imageView);
}
return super.getScaleType();
}
@Override
public ImageView getWrappedView() {
return (ImageView) super.getWrappedView();
}
@Override
protected void setImageDrawableInto(Drawable drawable, View view) {
((ImageView) view).setImageDrawable(drawable);
if (drawable instanceof AnimationDrawable) {
((AnimationDrawable) drawable).start();
}
}
@Override
protected void setImageBitmapInto(Bitmap bitmap, View view) {
((ImageView) view).setImageBitmap(bitmap);
}
private static int getImageViewFieldValue(Object object, String fieldName) {
int value = 0;
try {
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = (Integer) field.get(object);
if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
value = fieldValue;
}
} catch (Exception e) {
L.e(e);
}
return value;
}
}
(3)接下来我们看Download包,这个包下面只有两个类,一个是ImageDownloader接口,定义了一些方法,还有根据uri拿到流,还定义了一些网络请求的参数。
public interface ImageDownloader {
/**
根据uri 拿到流
*/
InputStream getStream(String imageUri, Object extra) throws IOException;
/** Represents supported schemes(protocols) of URI. Provides convenient methods for work with schemes and URIs.
* 支持的网络请求协议类型
* */
public enum Scheme {
HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
private String scheme;
private String uriPrefix;
Scheme(String scheme) {
this.scheme = scheme;
uriPrefix = scheme + "://";
}
/**判断类型
*/
public static Scheme ofUri(String uri) {
if (uri != null) {
for (Scheme s : values()) {
if (s.belongsTo(uri)) {
return s;
}
}
}
return UNKNOWN;
}
//
private boolean belongsTo(String uri) {
return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
}
/** Appends scheme to incoming path //获取全路径 */
public String wrap(String path) {
return uriPrefix + path;
}
/** 获取剔除前缀的路径 */
public String crop(String uri) {
if (!belongsTo(uri)) {
throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]", uri, scheme));
}
return uri.substring(uriPrefix.length());
}
}
}
(4)我们再来看一下display这个包,这个包里面是一个接口BitmapDisplayer,和他们的实现类,包括环形展示器,渐入渐出展示器,圆角展示器,还有一个简单的展示器,因为这个不是重点,所以我们看一下接口和实现就可以了。
public interface BitmapDisplayer {
/**一个接口 要一个bitmap 要一个
*/
void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom);
}
public final class SimpleBitmapDisplayer implements BitmapDisplayer {
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
imageAware.setImageBitmap(bitmap);
}
}
(5)我们看下面一个包deocde,这个是一个编译包,将图片转化为Bitmap,这个包下面包含一个接口ImageDecoder,BaseImageDecoder实现类,ImageDecodingInfo实体类
接口非常简单
public interface ImageDecoder {
/***
*
* @param imageDecodingInfo
* @return
* @throws IOException
*/
Bitmap decode(ImageDecodingInfo imageDecodingInfo) throws IOException;
}
包含图片编码转化的信息
public class ImageDecodingInfo {
private final String imageKey;
private final String imageUri;
private final String originalImageUri;
private final ImageSize targetSize;
private final ImageScaleType imageScaleType;//缩放类型
private final ViewScaleType viewScaleType;
private final ImageDownloader downloader;//图片下载器
private final Object extraForDownloader;//
private final boolean considerExifParams;
private final Options decodingOptions;
public ImageDecodingInfo(String imageKey, String imageUri, String originalImageUri, ImageSize targetSize, ViewScaleType viewScaleType,
ImageDownloader downloader, DisplayImageOptions displayOptions) {
this.imageKey = imageKey;
this.imageUri = imageUri;
this.originalImageUri = originalImageUri;
this.targetSize = targetSize;
this.imageScaleType = displayOptions.getImageScaleType();
this.viewScaleType = viewScaleType;
this.downloader = downloader;
this.extraForDownloader = displayOptions.getExtraForDownloader();
considerExifParams = displayOptions.isConsiderExifParams();
decodingOptions = new Options();
copyOptions(displayOptions.getDecodingOptions(), decodingOptions);
}
private void copyOptions(Options srcOptions, Options destOptions) {
destOptions.inDensity = srcOptions.inDensity;
destOptions.inDither = srcOptions.inDither;
destOptions.inInputShareable = srcOptions.inInputShareable;
destOptions.inJustDecodeBounds = srcOptions.inJustDecodeBounds;
destOptions.inPreferredConfig = srcOptions.inPreferredConfig;
destOptions.inPurgeable = srcOptions.inPurgeable;
destOptions.inSampleSize = srcOptions.inSampleSize;
destOptions.inScaled = srcOptions.inScaled;
destOptions.inScreenDensity = srcOptions.inScreenDensity;
destOptions.inTargetDensity = srcOptions.inTargetDensity;
destOptions.inTempStorage = srcOptions.inTempStorage;
if (Build.VERSION.SDK_INT >= 10) copyOptions10(srcOptions, destOptions);
if (Build.VERSION.SDK_INT >= 11) copyOptions11(srcOptions, destOptions);
}
@TargetApi(10)
private void copyOptions10(Options srcOptions, Options destOptions) {
destOptions.inPreferQualityOverSpeed = srcOptions.inPreferQualityOverSpeed;
}
@TargetApi(11)
private void copyOptions11(Options srcOptions, Options destOptions) {
destOptions.inBitmap = srcOptions.inBitmap;
destOptions.inMutable = srcOptions.inMutable;
}
public String getImageKey() {
return imageKey;
}
/** @return Image URI for decoding (usually image from disk cache) */
public String getImageUri() {
return imageUri;
}
/** @return The original image URI which was passed to ImageLoader */
public String getOriginalImageUri() {
return originalImageUri;
}
/**
* @return Target size for image. Decoded bitmap should close to this size according to {@linkplain ImageScaleType
* image scale type} and {@linkplain ViewScaleType view scale type}.
*/
public ImageSize getTargetSize() {
return targetSize;
}
/**
* @return {@linkplain ImageScaleType Scale type for image sampling and scaling}. This parameter affects result size
* of decoded bitmap.
*/
public ImageScaleType getImageScaleType() {
return imageScaleType;
}
/** @return {@linkplain ViewScaleType View scale type}. This parameter affects result size of decoded bitmap. */
public ViewScaleType getViewScaleType() {
return viewScaleType;
}
/** @return Downloader for image loading */
public ImageDownloader getDownloader() {
return downloader;
}
/** @return Auxiliary object for downloader */
public Object getExtraForDownloader() {
return extraForDownloader;
}
/** @return <b>true</b> - if EXIF params of image should be considered; <b>false</b> - otherwise */
public boolean shouldConsiderExifParams() {
return considerExifParams;
}
/** @return Decoding options */
public Options getDecodingOptions() {
return decodingOptions;
}
}
(6)最后我们来看一下assist这个包,里面包含了一个deque的包还有其他类型,这些类型主要包括一些缩放的类型定义,处理队列定义,从何处加载定义,图片大小实体类定义,图片缩放类型定义,流刷定义,错误实体类定义,内容输入流长度定义。由于都比较简单易懂,我直接把代码放上来。
//图片缩放的类型
public enum ViewScaleType {
FIT_INSIDE,
CROP;
//判断类型
public static ViewScaleType fromImageView(ImageView imageView) {
switch (imageView.getScaleType()) {
case FIT_CENTER:
case FIT_XY:
case FIT_START:
case FIT_END:
case CENTER_INSIDE:
return FIT_INSIDE;
case MATRIX:
case CENTER:
case CENTER_CROP:
default:
return CROP;
}
}
}
/**
* Queue processing type which will be used for display task processing
* 请求队列的处理类型
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.6.3
*/
public enum QueueProcessingType {
FIFO, LIFO
}
/**
* Source image loaded from.
*一个枚举 里面是图片获取的状态
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public enum LoadedFrom {
NETWORK, DISC_CACHE, MEMORY_CACHE
}
/**
* Present width and height values
*图片大小的实体类
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class ImageSize {
private static final int TO_STRING_MAX_LENGHT = 9; // "9999x9999".length()
private static final String SEPARATOR = "x";
private final int width;
private final int height;
public ImageSize(int width, int height) {
this.width = width;
this.height = height;
}
public ImageSize(int width, int height, int rotation) {
if (rotation % 180 == 0) {
this.width = width;
this.height = height;
} else {
this.width = height;
this.height = width;
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
/** Scales down dimensions in <b>sampleSize</b> times. Returns new object. */
public ImageSize scaleDown(int sampleSize) {
return new ImageSize(width / sampleSize, height / sampleSize);
}
/** Scales dimensions according to incoming scale. Returns new object. */
public ImageSize scale(float scale) {
return new ImageSize((int) (width * scale), (int) (height * scale));
}
@Override
public String toString() {
return new StringBuilder(TO_STRING_MAX_LENGHT).append(width).append(SEPARATOR).append(height).toString();
}
}
/**
*图片缩放的类型
*/
public enum ImageScaleType {
/** Image won't be scaled */
NONE,
/**
*/
NONE_SAFE,
/**
*/
IN_SAMPLE_POWER_OF_2,
/**
*/
IN_SAMPLE_INT,
/**
*/
EXACTLY,
/**
*/
EXACTLY_STRETCHED
}
/**但是BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法获取完整的数据.这是因为google对于InputStream流有个小bug,
在慢速网络的情况下可能产生中断。我们可以考虑重写FilterInputStream处理skip方法来解决这个bug。
这里我们通过继承FilterInputStream类的skip方法来强制实现flush流中的数据,
主要原理就是检查是否到文件末端,告诉http类是否继续。
*/
public class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int by_te = read();
if (by_te < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
/**
*请求失败的实体类
*/
public class FailReason {
private final FailType type; //失败的类型
private final Throwable cause;//错误
public FailReason(FailType type, Throwable cause) {
this.type = type;
this.cause = cause;
}
public FailType getType() {
return type;
}
public Throwable getCause() {
return cause;
}
public static enum FailType {
/** Input/output error. Can be caused by network communication fail or error while caching image on file system. */
IO_ERROR,
/**
* Error while
* decode image to Bitmap}
*/
DECODING_ERROR,
/**
* downloads are denied} and requested image wasn't cached in disk cache before.
*/
NETWORK_DENIED,
/** Not enough memory to create needed Bitmap for image */
OUT_OF_MEMORY,
/** Unknown error was occurred while loading image */
UNKNOWN
}
}
public class ContentLengthInputStream extends InputStream {
private final InputStream stream;
private final int length;
public ContentLengthInputStream(InputStream stream, int length) {
this.stream = stream;
this.length = length;
}
@Override
public int available() {
return length;
}
@Override
public void close() throws IOException {
stream.close();
}
@Override
public void mark(int readLimit) {
stream.mark(readLimit);
}
@Override
public int read() throws IOException {
return stream.read();
}
@Override
public int read(byte[] buffer) throws IOException {
return stream.read(buffer);
}
@Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
return stream.read(buffer, byteOffset, byteCount);
}
@Override
public void reset() throws IOException {
stream.reset();
}
@Override
public long skip(long byteCount) throws IOException {
return stream.skip(byteCount);
}
@Override
public boolean markSupported() {
return stream.markSupported();
}
}