Universal-Image-Loader源码分析(二)——载入图片的过程分析
之前的文章,在上面建立完config之后,UIl通过ImageLoader.getInstance().init(config.build());
来初始化ImageLoader对象,之后就可以用ImageLoader来加载图片。
这里,采用到单例模式来获取ImageLoader对象,保证他全局初始化一次。再上面的分析中,我们可以看出单例模式的好处,创建ImageLoader对象的时候需要创建Config,而Config里面同样初始化了一堆对象。如果每次用到都现初始化ImageLoader,消耗太大。我们看一下ImageLoader的init的源码
public synchronized void init(ImageLoaderConfiguration configuration) {
if (configuration == null) {
throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
}
if (this.configuration == null) {
L.d(LOG_INIT_CONFIG);
//创建图片加载引擎
engine = new ImageLoaderEngine(configuration);
this.configuration = configuration;
} else {
L.w(WARNING_RE_INIT_CONFIG);
}
}
如果该对象还没有config,则将传入的config赋值给this.config。并且初始化图片加载引擎。
我们继续看图片加载引擎主要执行的工作。
class ImageLoaderEngine {
/*ImageLoader加载配置*/
final ImageLoaderConfiguration configuration;
/*任务执行者*/
private Executor taskExecutor;
/*图片缓存任务执行则*/
private Executor taskExecutorForCachedImages;
/*任务分配者*/
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();
/**
* ImageLoader引擎构造器
* @param configuration
*/
ImageLoaderEngine(ImageLoaderConfiguration configuration) {
//初始化ImageLoader配置参数
this.configuration = configuration;
//初始化三个不同任务的执行者
taskExecutor = configuration.taskExecutor;
taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;
taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
}
/** Submits task to execution pool */
/**
* 提交图片加载和显示任务到执行线程池中,进行运行
* @param task 具体需要执行的任务
*/
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来进行执行任务
taskExecutorForCachedImages.execute(task);
} else {
//当天图片在本地文件系统中没有缓存,直接采用taskExecutor来进行执行任务
taskExecutor.execute(task);
}
}
});
}
/**
* Submits task to execution pool
* 提交图片显示任务并且执行 (该图片从内存缓存中取得)
*/
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();
}
}
/**
* 进行创建任务执行者
* @return
*/
private Executor createTaskExecutor() {
return DefaultConfigurationFactory
.createExecutor(configuration.threadPoolSize, configuration.threadPriority,
configuration.tasksProcessingType);
}
/**
* 获取当前被加载ImageAware到图片的地址
* Returns URI of image which is loading at this moment into passed {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}
*/
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>.
*
* @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} for which display task
* 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
* isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired
* with {@link FailReason.FailType#NETWORK_DENIED}
*
* @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);
}
/**
* Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle <a
* href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not.
*
* @param handleSlowNetwork pass <b>true</b> - to use {@link FlushedInputStream} for network downloads; <b>false</b>
* - 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();
}
}
/**
* 停止ImageLoader引擎,取消所有正在运行或者挂起的图片显示任务,并且清除内部的数据
* Stops engine, cancels all running and scheduled display image tasks. Clears internal data.
* <br />
* <b>NOTE:</b> This method doesn't shutdown
* {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
* 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();
}
}
上面的代码中,核心的部分就是创建了任务执行器和图片缓存执行器,并且在submit方法中针对提交的任务,选择不同的执行器执行。
ImageLoader关于加载显示图片,有如下几种用法,我们依次分析一下。
displayImage(), loadImage()
先看loadImage()
下面的loadImage所有的重载方法。
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) {
}
不管是几个参数的loadImage,最后都会重载下面的方法。
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);
}
如果没有指定targetImageSize以及options就会采用config默认的提供,然后根据targetImageSize以及uri生成一个NonViewAware,最终通过displayImage来加载。
targetImageSize是一个ImageSize对象,该类是image尺寸的封装类,config的默认值是屏幕的宽高。
options是采用config默认创建,config的默认值是faultDisplayImageOptions = DisplayImageOptions.createSimple();
这个初始化都是创建的DisplayImageOptions都是默认值,基本什么属性都是false或者null或者0
接下来,我们看一下NonViewAware类,NonViewAware是实现了ImageAware接口的一个类,ImageAware接口主要定义了图片处理和显示所需要的方法和属性。所以NonViewAware也只是对传入的参数进行封装,来提供一个外部访问的接口。
真正显示图片的方法都是displayImage,displayImage方法和loadImage一样,提供了多种参数,displayImage的重载方法要多一些,因为displayImage方法有一类是接受ImageView而另一类是接受ImageAware。
下面是无论如何都是最终调用的方法:
代码如下:
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
//进行检查ImageLoader全局相关配置
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)) {
engine.cancelDisplayTaskFor(imageAware);
//接口方法回调,当前图片加载任务开始
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//进行判断是否给imageview添加一个空地址的资源图片
if (options.shouldShowImageForEmptyUri()) {
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
imageAware.setImageDrawable(null);
}
//直接加载回调加载成功
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}
//=============图片地址存在=====================
if (targetSize == null) {
//如果图片显示的目标大小没有设置的,那么就使用默认大小尺寸即可
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
}
//根据地址和图片目标尺寸信息,生成缓存key
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
//开始进行加载图片
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//首先根据key去缓存中获取是否还存在该图片
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
//缓存中该图片存在
L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
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 {
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));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}
具体的执行过程已经通过代码注释了,下面梳理下流程。
首先,针对为null的属性,初始化这些属性,然后判断传入的uri是不是为空,如果为空,则根据options.shouldShowImageForEmptyUri来判断是否显示先设置好的图片。
如果传入的uri是存在的,则根据地址和图片目标尺寸的信息来生成缓存key,然后执行
prepareDisplayTaskFor方法。
再根据key来判断缓存中是否存在该图片,如果存在,就判断options的shouldPostProcess,如果为true就创建一个ProcessAndDisplayImageTask对象,通过图片加载引擎来加载。如果返回的值false,则调用options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
如果缓存不存在,则直接通过网络加载该图片,加载的方式是构建LoadAndDisplayImageTask对象,通过图片加载引擎来判断。
在这里,关于图片的缓存相关的内容,先不分析。接下来主要分析的是图片如何执行这两种不同的任务的。
第一种,在有缓存的情况下,通过判断shouldPostProcess为true来让图片引擎处理任务,这里的shouldPostProcess是指在拿到bitmap之后是否进行后续的操作,判断标准就是postProcess是否为null.
如果不为null,也就是shouldPostProcess为true,则执行下面的代码:
下面,我们来看一下,图片引擎是如何执行ProcessAndDisplayImageTask。
void submit(ProcessAndDisplayImageTask task) {
initExecutorsIfNeed();
taskExecutorForCachedImages.execute(task);
}
内部直接调用taskExecutorForCachedImages去执行task,所以主要看ProcessAndDisplayImageTask的task构造。
final class ProcessAndDisplayImageTask implements Runnable {
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
/*ImageLoader引擎*/
private final ImageLoaderEngine engine;
private final Bitmap bitmap;
/*ImageLoader信息封装对象*/
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
/**
* 图片处理显示任务构造器
* @param engine
* @param bitmap
* @param imageLoadingInfo
* @param 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() {
L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey);
//获取图片处理器 然后取得加载的图片
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);
}
}
这边可以看出,task内部run方法里面,首先得到一个BitmapProcessor,然后通过该processor去处理bitmap,然后将处理后的bitmap以及其他信息封装成了DisplayBitmapTask,然后最终还是执行了LoadAndDisplayImageTask的runTask方法
下面将看LoadAndDisplayImageTask.runTask方法
static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
if (sync) {
//如果同步 任务直接运行
r.run();
} else if (handler == null) {
engine.fireCallback(r);
} else {
//任务通过Handler分发到主线程执行
handler.post(r);
}
}
这边,直接在UI线程displayBitmapTask,后面再看displayBitmapTask的内部实现。
回到shouldPostProcess的判断那里,如果为false,则直接调用BitmapDisplay显示图片,这里传入的是SimpleBitmapDisplayer
再回到缓存判断那里,上面的代码都是在有内存缓存的情况下,执行的。看一下在无内存缓存时,执行的细节。
//缓存中不存在该图片 通过网络加载
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));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
通过源码可以看出,首先判断要不要进行显示加载中的View,然后构建图片加载信息,通过图片加载信息构建LoadAndDisplayImageTask对象,执行去run方法。
上面,我们已经分析了其runTask方法,该方法比较简单,这次我们看一下run方法。
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 {
//从缓存中获取到图片信息
//设置图片来源信息 --Memory Cache
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);
}
其流程图如下:
可以看到这边,利用的图片三级缓存,第一级是内存缓存,如果内存缓存没有则利用二级缓存,从文件中去读取,如果文件中有,则从文件中取出bitmap,如果没有则从网络下载。
这边从文件中bitmap的方法是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对象
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;
}
首先是从diskcache中,取出File,然后对file进行转换。如果转换后的bitmap为null,则从网络获取图片,获取图片的方法调用是在options.isCacheOnDisk() && tryCacheImageOnDisk()
该判断首先判断是否存储在文件中,如果不存在文件中,就不执行后面的网络获取。
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;
}
downloadImage是执行网络请求的方法,其内部通过BaseImageDownloader
进行下载,其内部的网络库是HttpURLConnection
,下载后的图片,根据设定的文件存储的最大宽高,进行缩放与保存。
这样,就在文件缓存中缓存了图片,在回到上面LoadAndDisplayTask的run方法,在得到bitmap之后,就会判断是否要 对bitmap进行预处理,预处理完的bitmap全部会缓存到内存缓存中。上面的操作都是建立在bitmap中内存缓存中取没有取出来的情况,如果取出来就直接得到bitmap,然后从bitmap判断是否进行后续的处理。
这里,简单说一下 preProcessor以及postProcessor,preProcessor是指对图片进行预处理,比如加水印,如果加水印的图片都会缓存到内存,postProcessor是对取出的bitmap做一些后续的操作,操作后将显示出来。
最后得到的Bitmap会封装成DisplayBitmapTask,调用上面提到的runtask方法,进行处理。
这样,到此,发起图片获取需求,到图片经过内存缓存,文件缓存,网络获取后得到,然后再通过Handler回到UI线程的流程就分析完毕了。
其实,整个流程非常的简单,清晰。只不过在考虑到了多种情况,使得代码看上去很多。
下面,将分析图片加载框架最重要的一部分,缓存的设计。
Universal-Image-Loader源码分析(二)——载入图片的过程分析的更多相关文章
- Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题
4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...
- 框架-springmvc源码分析(二)
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
- Tomcat源码分析二:先看看Tomcat的整体架构
Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...
- 十、Spring之BeanFactory源码分析(二)
Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...
- Vue源码分析(二) : Vue实例挂载
Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...
- 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>
目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...
- spring源码分析(二)Aop
创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...
- ConcurrenHashMap源码分析(二)
本篇博客的目录: 一:put方法源码 二:get方法源码 三:rehash的过程 四:总结 一:put方法的源码 首先,我们来看一下segment内部类中put方法的源码,这个方法它是segment片 ...
- spark(1.1) mllib 源码分析(二)-相关系数
原创文章,转载请注明: 转载自http://www.cnblogs.com/tovin/p/4024733.html 在spark mllib 1.1版本中增加stat包,里面包含了一些统计相关的函数 ...
随机推荐
- 数据库中存储日期的字段类型到底应该用varchar还是datetime
将数据库中存储时间的数据类型改为varchar(),这时最好让这些时间是数据库中自动生成的(一个没有格式的输入也可能会导致输出错误),因为存储类型为varchar(),所以获取到的值也就被认为是一个字 ...
- pdf.js 使用汇总
https://www.cnblogs.com/iPing9/p/7154753.htmlhttp://blog.csdn.net/m0_38021128/article/details/708684 ...
- thinphp 缓存机制导致代码不跟新
问题: 调试阶段,程序已经更新,但是浏览器没有出现新效果! 1.以为是谷歌浏览器缓存导致,解决:设置--高级设置--隐私设置--清除浏览器缓存 一小时过去了,但还是没有更新,怎么刷新都没用!! 2. ...
- [css3] 看博客学习别人的旋转的星球
定义一个div 太阳轨道sunline,边框显示出来,定义position为relative #sunline{ width: 500px; height: 500px; border:2px sol ...
- Android-Handler使用姿势
http://www.jianshu.com/p/8e9a54f1826e 好文章先马,慢慢看
- HDU1054(KB10-H 最小顶点覆盖)
Strategic Game Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 微信小程序传参数的几种方法
1,navigator 跳转时 wxml页面(参数多时可用“&”) <navigator url='../index/index?id=1&name=aaa'></n ...
- MySQL入门详解(三)---mysql如何进行主从配置
基本要求 两台服务器(windows,linux,mac) 双方mysql版本需一致,如不一致,只要主节点低于从节点 两台服务器防火墙关闭 双方数据库所用的用户,要具有远程访问的权限 主服务器配置 修 ...
- BZOJ5322: [JXOI2018]排序问题
传送门 不难看出期望就是 \(\frac{(n+m)!}{\prod_{v=1}^{max}(cnt_v!)}\),\(cnt_v\) 表示 \(v\) 这个数出现的次数. 贪心就是直接把 \(m\) ...
- PHP-隐藏手机号中间四位
substr_replace('手机号', '****', 3, 4);