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依然是使用我们比较熟悉的内存缓存-硬盘缓存-网络加载模式。通过对加载过程与相关数据的封装,增强了程序的可扩展性。通过不同的类管理不同的线程以及不同的功能,达到灵活修改功能的目的,使程序方便扩展。今天就写到这里吧。相关文章推荐
- 关于Android中RSA数字签名的理解及使用
- Android:代码修改layout_marginBottom的属性
- Android ListView分页加载(服务端+android端)De
- 光标 与 输入法 之 android:imeOptions属性
- Android 虚线分割线
- 解决一些android studio 中的编码问题
- Android中几种常用的定时器和延时方法
- Android的按钮单击事件及监听器的实现方式
- ubuntu下Qt之android环境配置以及一些常见问题解决
- android坐标
- Android SDK 开发范例大全 ---3.8手机页面的转换
- Android 换肤功能的实现(Apk插件方式)
- 将Eclipse代码导入到AndroidStudio的两种方式
- android之解析json数据格式详解
- Android Context原理与使用的总结
- Android LayoutAnimation
- Android EditText 软键盘上回车改为搜索
- android手电筒
- Android利用WebSocket进行实时通讯
- Android keystore相关