您的位置:首页 > 移动开发 > Android开发

Android知识总结:Universal-Imageloader学习笔记2 主业务流程源码分析

2015-12-01 19:25 691 查看

主业务流程

先看一张官方readme中的示例图



由上图可见,图片的缓存分为内存缓存,硬盘缓存,当硬盘中没有相应数据时,我们需要从网络上下载图片,这种流程符合一般情况下我们对图片缓存的认识。当显示图片时,无论网络加载还是硬盘加载,都需要先缓存入内存,在从内存中取出显示到图片控件。另外需要注意的是框架为我们提供了两个图片处理流程,PreProcess以及PostProcess,这两个流程在加载图片的options中设置回调。其中PreProcess是在图片解压完毕时进行的处理,也就是说如果设置了PreProcess,存入内存的图片都会经过这个流程。PostPrces是在显示到控件上时进行的处理。

框架目录结构与功能



源码主要分为三个文件夹,其中core中是主要业务逻辑和数据结构,cache主要是用于管理硬盘与内存存储,utils提供了一些工具类。

主业务流程代码分析

1.ImageLoader的主要逻辑

displayImage方法进行图片的加载,下面我们着重看一下这个方法的实现,其中包括
LoadAndDisplayImageTask作为图片硬盘加载与网络加载的任务,ProcessAndDisplayImageTask作为缓存中加载图片的任务,具体过程见注释。

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;
}
//如果Uri为空
if (TextUtils.isEmpty(uri)) {
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//如果设置了Uri为空的默认图片
if (options.shouldShowImageForEmptyUri()) {
//设置空Uri默认图
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
//设置为空
imageAware.setImageDrawable(null);
}
//回调加载成功
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}

if (targetSize == null) {
//获取容器(ImageView)的长宽
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
}

//*****************生成内存中使用的键 并与 ImageView及其相关图片显示数据(imageAware)绑定********
//使用uri和大小生成 内存中的键
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);

//在ImageLoadEngine中 建立imageAware(图片信息,包括ImageView及其所对应的图片) 和 键(内存索引键) 的关系
//ImageLoadEngine (姑且称作下载引擎) 中维护
//     1.下载用到的线程
//     2.imageAware 和 键 的关系
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);

//**********************开始加载********************

//监听开始下载
listener.onLoadingStarted(uri, imageAware.getWrappedView());

//尝试用上面生成的键在内存中获取Bitmap
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);

if (bmp != null && !bmp.isRecycled()) {
L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
//在内存中成功取出图片

if (options.shouldPostProcess()) {
//如果options中设置了postProcessor,即显示图片前需要对图片进行一定的处理
//生成图片加载信息
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
//调用options中设置的postProcess处理图片,并显示图片
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
defineHandler(options));

//同步或者异步执行displayTask,加载并显示图片
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
} else {
//直接显示图片,并回调正在显示接口
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 imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));

//从硬盘或者网络加载图片
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));

//同步或者异步执行displayTask,加载并显示图片
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}

2.LoadAndDisplayImageTask

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();

//尝试从内存中根据Bitmap的键加载
bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {
//从硬盘或者网络中加载!!!!!!!
bmp = tryLoadBitmap();
if (bmp == null) return; // listener callback already was fired

checkTaskNotActual();
checkTaskInterrupted();

//调用preProcessor进行图片处理   preProcessor处理完的图放入内存
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);
}

//如何设置了PostProcessor,则执行PostProcessor    PostProcessor是从内存中拿出后进行的处理
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);
}

3.tryLoadBitmap() 真正加载使用的方法

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(Scheme.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 = Scheme.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;
}

3.ProcessAndDisplayImageTask

内存加载类功能相对简单,只需要进行一次PostProcess,然后显示到控件即可。
@Override
public void run() {
L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey);

//执行postProcessor
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);
}

总结

可以ImageLoader依然是使用我们比较熟悉的内存缓存-硬盘缓存-网络加载模式。通过对加载过程与相关数据的封装,增强了程序的可扩展性。通过不同的类管理不同的线程以及不同的功能,达到灵活修改功能的目的,使程序方便扩展。今天就写到这里吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: