从代码分析Android-Universal-Image-Loader的图片加载、显示流程
来源:互联网 发布:蜂窝通信网络定位技术 编辑:程序博客网 时间:2024/06/08 07:09
从UNIVERSAL IMAGE LOADER. PART 3(四个DisplayImage重载方法详解)中,我们学习了Android-Universal-Image-Loader(以下简称UIL)中四个DisplayImage重载方法的使用,如果你还没有学习,最好先返回去看看,不然可能不理解这篇文章。在这篇文章中我们将主要探讨Android-Universal-Image-Loader的主要流程和这些流程相关的类的分析。
我们先了解一下UIL加载图片的流程(可以通过查看ImageLoader.displayImage(…)方法分析得出),如下图:
从上图中,我们可以看出,UIL加载图片的一般流程是先判断内存中是否有对应的Bitmap,再判断磁盘(disk)中是否有,如果没有就从网络中加载。最后根据原先在UIL中的配置判断是否需要缓存Bitmap到内存或磁盘中。Bitmap加载完后,就对它进行解析,然后显示到特定的ImageView中。
有了对UIL对图片加载和处理流程的初步认识之后,我们就可以着手分析它的源代码了。先从ImageLoader.displayImage(...)入手,毕竟一切都因它而始。
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { //检查UIL的配置是否被初始化 checkConfiguration(); if (imageAware == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { listener = emptyListener; } if (options == null) { options = configuration.defaultDisplayImageOptions; } if (TextUtils.isEmpty(uri)) { engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView()); if (options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources)); } else { imageAware.setImageDrawable(null); } listener.onLoadingComplete(uri, imageAware.getWrappedView(), null); return; } //计算Bitmap的大小,以便后面解析图片时用 ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); 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()) { 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); } } }
代码有点多,但是有很多代码是进行异常判断处理和函数的回调,为了先把握整体的流程,我们先放弃细节方面的追踪。基本上重要的处理流程我都有用注释标出。不过,从这段代码中我们也可以看出这段代码的结构非常清晰。对图片的整个的加载流程都有对应的监听接口(ImageLoadingListener.onLoadingStarted,ImageLoadingListener.onLoadingComplete,ImageLoadingListener这个类就是用来监听图片的加载过程的),也就是说整个的图片加载过程程序员都可以进行相应的处理。我们先关注一下图片从无到有的加载过程,毕竟这部分是大家最为关心的。看到第63行中的LoadAndDisplayImageTask,跟进LoadAndDisplayImageTask.run()方法中。在这个run()方法中,除了 bmp = tryLoadBitmap();这一句是对图片进行加载,其他的函数都是对Bitmap进行处理或者显示。我们继续进入看看。
private Bitmap tryLoadBitmap() throws TaskCancelledException { Bitmap bitmap = null; try { //尝试从磁盘缓存中读取Bitmap File imageFile = configuration.diskCache.get(uri); if (imageFile != null && imageFile.exists()) { 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~12行是尝试从磁盘缓存中加载Bitmap。第19行判断磁盘中是否有缓存,就开始进行网络下载(tryCacheImageOnDisk())。在tryCacheImageOnDisk()函数中有个tryCacheImageOnDisk()的 loaded = downloadImage()这行进行图片下载。
private boolean downloadImage() throws IOException { InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader()); return configuration.diskCache.save(uri, is, this); }这个函数做的事情很简单,就是获取一个实现Image Downloader的downloader(当然这里,作者根据网络情况将downloader分为慢速(slowNetworkDownloader)、正常速度(downloader)、网络拒绝(networkDeniedDownloader)情况下的download,在这里我们不展开,你只要知道他们是imageDownloader接口的实现者就行,后面的文章会探讨这个问题),然后利用Disk Cache将Bitmap写入磁盘缓存中。返回到之前我们进入downloadImage()函数中的tryLoadBitmap(),在将图片缓存到磁盘中。是否缓存到磁盘跟配置有关)后,紧接着调用bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));解析图片。进入decodeImage()函数中,我们发现UIL调用Image Decoder进行图片的解析。
1 private Bitmap decodeImage(String imageUri) throws IOException {2 ViewScaleType viewScaleType = imageAware.getScaleType();3 ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,4 getDownloader(), options);5 return decoder.decode(decodingInfo);6 }decode()函数最终是调用BaseImageDecoder.decode()方法进行解析的,这个利用之前获得的inputStream,直接从它身上读取数据,然后进行解析,并对整个下载任务的网络接口进行重置。
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { Bitmap decodedBitmap; ImageFileInfo imageInfo; InputStream imageStream = getImageStream(decodingInfo); try { imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo); imageStream = resetStream(imageStream, decodingInfo); Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo); decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions); } finally { IoUtils.closeSilently(imageStream); } if (decodedBitmap == null) { L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey()); } else { decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal); } return decodedBitmap; }
接下来,有了解析好的Bitmap对象后,剩下的就是在Image View对象中显示它了。我们回到文章一开始介绍到的ImageLoader.displayImage(...)函数中(相关的代码在文章的开头处可以看到)。
为了方便,我还是将ImageLoader.displayImage(...)中涉及的代码贴在下面。
1 DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);2 runTask(displayBitmapTask, syncLoading, handler, engine);
我们进去DisplayBitmapTask.run()函数中看看。除去前面几行的ImageLoadingListener.ImageLoadingListener()代码,相关代码其实就一行 displayer.display(bitmap, imageAware, loadedFrom),它其实就是调用BitmapDisplayer这个对象将Bitmap对象显示到ImageView上。根据实现BitmapDisplayer接口的不同对象,还有SimpleBitmapDisplayer、FadeInBitmapDisplayer、RoundedBitmapDisplayer、RoundedVignetteBitmapDisplayer这5种对象。
最后,让我们用任务流图概况以上的处理流程中对应接口
0 0
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- Android 开源框架Universal-Image-Loader完全解析(五)- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- Android-Universal-Image-Loader的图片加载、显示流程
- Universal-Image-Loader的图片加载流程源码分析
- Android-Universal-Image-Loader 图片加载库 详细分析
- Android-Universal-Image-Loader 图片加载库 详细分析
- android 平台上加载、缓存,显示图片的开源代码Android-Universal-Image-Loader
- Android-Universal-Image-Loader 异步加载图片
- 使用Android-Universal-Image-Loader加载图片
- Android-Universal-Image-Loader加载图片
- Android-Universal-Image-Loader 图片加载库
- 异步加载图片框架Android-Universal-Image-Loader的使用
- Android Universal-image-loader功能强大的图片加载框架
- Android图片异步加载框架Universal Image Loader的源码分析
- 游戏《扫雷》开发 用最笨的办法去开发 让每个人都能够明白原理 附赠源码g
- 开源华为Portal协议中间件
- UITextField详解
- leetcode 虐我篇之(十八)Binary Tree Level Order Traversal II
- 我用奔跑告诉你、不回头……
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- StyleManager.getStyleDeclaration FLEX4 报错
- dfgdfg的风格的风格的福德宫
- Java泛型的高级应用
- 网页设计中 utf-8和gb2312编码
- Codeforces 354C. Vasya and Beautiful Arrays【DP,暴力】
- 破解工具大全
- node fs文件操作
- BigDecimal问题