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

主业务流程


先看一张官方readme中的示例图
《Android知识总结:Universal-Imageloader学习笔记2 主业务流程源码分析》



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

框架目录结构与功能

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



源码主要分为三个文件夹,其中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依然是使用我们比较熟悉的内存缓存-硬盘缓存-网络加载模式。通过对加载过程与相关数据的封装,增强了程序的可扩展性。通过不同的类管理不同的线程以及不同的功能,达到灵活修改功能的目的,使程序方便扩展。今天就写到这里吧。



    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/lidec/article/details/50133533
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞