Android-Universal-Image-Loader 学习笔记(二)加载图片原理
2015-11-11 11:24
591 查看
Android-Universal-Image-Loader
学习笔记(二)
ImageAware:需要显示的ImageView
Options: 显示图片的配置,没有就是用默认的
ImageLoadingListenner:加载情况的监听,用户可以根据自己的情况重写
ImageLoadingProgressListener:加载进度条的情况,显示加载过程中的进度条百分比.
该方法用于设置滑动过程不加载图片,优化显示效果
PauseOnScrollListener源码如下:
在调用diplayImage的时候,我们会判断pause状态.
从上面的代码中,我们可以看出,它会将ImageView转换成ImageViewAware, ImageViewAware主要是做什么的呢?该类主要是将ImageView进行一个包装,将ImageView的强引用变成弱引用,当内存不足的时候,可以更好的回收ImageView对象,还有就是获取ImageView的宽度和高度。这使得我们可以根据ImageView的宽高去对图片进行一个裁剪,减少内存的使用。
先把握整体的流程,我们先放弃细节方面的追踪。基本上重要的处理流程我都有用注释标出。不过,从这段代码中我们也可以看出这段代码的结构非常清晰。对图片的整个的加载流程都有对应的监听接口(ImageLoadingListener.onLoadingStarted,ImageLoadingListener.onLoadingComplete,ImageLoadingListener这个类就是用来监听图片的加载过程的),也就是说整个的图片加载过程程序员都可以进行相应的处理。我们先关注一下图片从无到有的加载过程,毕竟这部分是大家最为关心的。看到第63行中的LoadAndDisplayImageTask,跟进LoadAndDisplayImageTask.run()方法中。在这个run()方法中,除了 bmp
= tryLoadBitmap();这一句是对图片进行加载,其他的函数都是对Bitmap进行处理或者显示。我们继续进入看看
如果waitIfPaused(), delayIfNeed()返回true的话,直接从run()方法中返回了,不执行下面的逻辑, 接下来我们先看看waitIfPaused()
这里用到了PauseOnScrollListener这个类,使用很简单ListView.setOnScrollListener(new PauseOnScrollListener(pauseOnScroll, pauseOnFling )), pauseOnScroll控制我们缓慢滑动ListView,GridView是否停止加载图片,pauseOnFling 控制猛的滑动ListView,GridView是否停止加载图片
除此之外,这个方法的返回值由isTaskNotActual()决定,我们接着看看isTaskNotActual()的源码
isViewCollected()是判断我们ImageView是否被垃圾回收器回收了,如果回收了,LoadAndDisplayImageTask方法的run()就直接返回了,isViewReused()判断该ImageView是否被重用,被重用run()方法也直接返回,为什么要用isViewReused()方法呢?主要是ListView,GridView我们会复用item对象,假如我们先去加载ListView,GridView第一页的图片的时候,第一页图片还没有全部加载完我们就快速的滚动,isViewReused()方法就会避免这些不可见的item去加载图片,而直接加载当前界面的图片.
在tryLoadBitmap()判断磁盘中是否有缓存,就开始进行网络下载(tryCacheImageOnDisk())。在tryCacheImageOnDisk()函数中有个tryCacheImageOnDisk()的 loaded
= downloadImage()这行进行图片下载。
获取一个实现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进行图片的解析。
decode()函数最终是调用BaseImageDecoder.decode()方法进行解析的,这个利用之前获得的inputStream,直接从它身上读取数据,然后进行解析,并对整个下载任务的网络接口进行重置。
接下来,有了解析好的Bitmap对象后,剩下的就是在Image View对象中显示它了。我们回到文章一开始介绍到的ImageLoader.displayImage(...)函数中。
我们进去DisplayBitmapTask.run()函数中看看。除去前面几行的ImageLoadingListener.ImageLoadingListener()代码,相关代码其实就一行 displayer.display(bitmap,
imageAware, loadedFrom),它其实就是调用BitmapDisplayer这个对象将Bitmap对象显示到ImageView上。根据实现BitmapDisplayer接口的不同对象,还有SimpleBitmapDisplayer、FadeInBitmapDisplayer、RoundedBitmapDisplayer等对象.
学习笔记(二)
1. 流程图
了解一下UIL加载图片的流程(可以通过查看ImageLoader.displayImage(…)方法分析得出),网上一大把流程图,个人觉得比较容易理解为以下两个:
图一
图二
从上图中,我们可以看出,UIL加载图片的一般流程是先判断内存中是否有对应的Bitmap,再判断磁盘(disk)中是否有,如果没有就从网络中加载。最后根据原先在UIL中的配置判断是否需要缓存Bitmap到内存或磁盘中。Bitmap加载完后,就对它进行解析,然后显示到特定的ImageView中。二、加载图片
Android-Universal-Image-Loader有五个重载方法:public void displayImage(String uri, ImageAware imageAware) { displayImage(uri, imageAware, null, null, null); } public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) { displayImage(uri, imageAware, null, listener, null); } public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) { displayImage(uri, imageAware, options, null, null); } public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener) { displayImage(uri, imageAware, options, listener, null); } public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener)
第一个方法到第四个方法最终都是调用第五个方法,直接看第五个方法的参数:
Uri:图片的Uri地址ImageAware:需要显示的ImageView
Options: 显示图片的配置,没有就是用默认的
ImageLoadingListenner:加载情况的监听,用户可以根据自己的情况重写
public interface ImageLoadingListener { /** * Is called when image loading task was started * 开始加载时候执行 * @param imageUri Loading image URI * @param view View for image */ void onLoadingStarted(String imageUri, View view); /** * Is called when an error was occurred during image loading *加载失败执行 * @param imageUri Loading image URI * @param view View for image. Can be <b>null</b>. * @param failReason {@linkplain com.nostra13.universalimageloader.core.assist.FailReason The reason} why image * loading was failed */ void onLoadingFailed(String imageUri, View view, FailReason failReason); /** * Is called when image is loaded successfully (and displayed in View if one was specified) * 加载完成执行 * @param imageUri Loaded image URI * @param view View for image. Can be <b>null</b>. * @param loadedImage Bitmap of loaded and decoded image */ void onLoadingComplete(String imageUri, View view, Bitmap loadedImage); /** * Is called when image loading task was cancelled because View for image was reused in newer task * 加载取消执行 * @param imageUri Loading image URI * @param view View for image. Can be <b>null</b>. */ void onLoadingCancelled(String imageUri, View view); }
ImageLoadingProgressListener:加载进度条的情况,显示加载过程中的进度条百分比.
public interface ImageLoadingProgressListener { /** * Is called when image loading progress changed. * * @param imageUri Image URI * @param view View for image. Can be <b>null</b>. * @param current Downloaded size in bytes * @param total Total size in bytes */ void onProgressUpdate(String imageUri, View view, int current, int total); }
还有一个常用的优化ListView或者GridView的监听:
ListView.setOnScrollListener(new PauseOnScrollListener(imageLoader,true,true));该方法用于设置滑动过程不加载图片,优化显示效果
PauseOnScrollListener源码如下:
public class PauseOnScrollListener implements OnScrollListener { private ImageLoader imageLoader; private final boolean pauseOnScroll; private final boolean pauseOnFling; private final OnScrollListener externalListener; /** * Constructor * * @param imageLoader {@linkplain ImageLoader} instance for controlling * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling */ public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) { this(imageLoader, pauseOnScroll, pauseOnFling, null); } /** * Constructor * * @param imageLoader {@linkplain ImageLoader} instance for controlling * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also * will be get scroll events */ public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling, OnScrollListener customListener) { this.imageLoader = imageLoader; this.pauseOnScroll = pauseOnScroll; this.pauseOnFling = pauseOnFling; externalListener = customListener; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: imageLoader.resume(); break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: if (pauseOnScroll) { imageLoader.pause(); } break; case OnScrollListener.SCROLL_STATE_FLING: if (pauseOnFling) { imageLoader.pause(); } break; } if (externalListener != null) { externalListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (externalListener != null) { externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } }
在调用diplayImage的时候,我们会判断pause状态.
三、从代码角度看ImageLoader加载显示过程
从ImageLoader.displayImage(…)方法分析:public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { //首先检测UIL的配置是否被初始化,在App的application中设置 checkConfiguration(); if (imageAware == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { // 设置一个默认的监听器 SimpleImageLoadingListener listener = emptyListener; } if (options == null) { // 当没有options,使用默认 options = configuration.defaultDisplayImageOptions; } if (TextUtils.isEmpty(uri)) { //当uri是null,显示默认图片或者不显示 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; } // 解析ImageView的大小,以便后面解析图片用 ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);<span style="font-family: Arial; font-size: 14px; line-height: 26px;">//ImageLoaderEngine中存在一个HashMap,用来记录正在加载的任务</span> listener.onLoadingStarted(uri, imageAware.getWrappedView()); // Bitmap 是否已经存在缓存 Bitmap bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp != null && !bmp.isRecycled()) { //Bitmap 存在cache缓存中,取出来用 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 { Log.d("wutingyou....", "ImageLoader from Cache"); // 显示图片,根据显示方式显示 默认SimpleBitmapDisplayer options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE); // 回调完成接口 listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp); } } else { //Bitmap 不存在cache缓存中,从文件中或者网络里面获取bitmap对象 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 { eca5 engine.submit(displayTask); } } }
从上面的代码中,我们可以看出,它会将ImageView转换成ImageViewAware, ImageViewAware主要是做什么的呢?该类主要是将ImageView进行一个包装,将ImageView的强引用变成弱引用,当内存不足的时候,可以更好的回收ImageView对象,还有就是获取ImageView的宽度和高度。这使得我们可以根据ImageView的宽高去对图片进行一个裁剪,减少内存的使用。
先把握整体的流程,我们先放弃细节方面的追踪。基本上重要的处理流程我都有用注释标出。不过,从这段代码中我们也可以看出这段代码的结构非常清晰。对图片的整个的加载流程都有对应的监听接口(ImageLoadingListener.onLoadingStarted,ImageLoadingListener.onLoadingComplete,ImageLoadingListener这个类就是用来监听图片的加载过程的),也就是说整个的图片加载过程程序员都可以进行相应的处理。我们先关注一下图片从无到有的加载过程,毕竟这部分是大家最为关心的。看到第63行中的LoadAndDisplayImageTask,跟进LoadAndDisplayImageTask.run()方法中。在这个run()方法中,除了 bmp
= tryLoadBitmap();这一句是对图片进行加载,其他的函数都是对Bitmap进行处理或者显示。我们继续进入看看
public void run() { /** * 如果这两者是true,直接返回 **/ // 这里用到了PauseOnScrollListener这个类,使用很简单ListView.setOnScrollListener(new PauseOnScrollListener(pauseOnScroll, pauseOnFling )), // pauseOnScroll控制我们缓慢滑动ListView,GridView是否停止加载图片,pauseOnFling 控制猛的滑动ListView,GridView是否停止加载图片 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(); // 再次从cache内存缓存中 bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp == null || bmp.isRecycled()) { // cache缓存中没有或者已经被回收,尝试从sd卡缓存或者网络中获取 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 { 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); }
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; //判断是否开启SD卡缓存,如果开启尝试缓存到SD上去 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; }
如果waitIfPaused(), delayIfNeed()返回true的话,直接从run()方法中返回了,不执行下面的逻辑, 接下来我们先看看waitIfPaused()
private boolean waitIfPaused() { AtomicBoolean pause = engine.getPause(); if (pause.get()) { synchronized (engine.getPauseLock()) { if (pause.get()) { L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey); try { engine.getPauseLock().wait(); } catch (InterruptedException e) { L.e(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey); } } } // isViewCollected()是判断我们ImageView是否被垃圾回收器回收了,如果回收了, // LoadAndDisplayImageTask方法的run()就直接返回了, // isViewReused()判断该ImageView是否被重用,被重用run()方法也直接返回 return isTaskNotActual(); }
这里用到了PauseOnScrollListener这个类,使用很简单ListView.setOnScrollListener(new PauseOnScrollListener(pauseOnScroll, pauseOnFling )), pauseOnScroll控制我们缓慢滑动ListView,GridView是否停止加载图片,pauseOnFling 控制猛的滑动ListView,GridView是否停止加载图片
除此之外,这个方法的返回值由isTaskNotActual()决定,我们接着看看isTaskNotActual()的源码
private boolean isTaskNotActual() { return isViewCollected() || isViewReused(); }
isViewCollected()是判断我们ImageView是否被垃圾回收器回收了,如果回收了,LoadAndDisplayImageTask方法的run()就直接返回了,isViewReused()判断该ImageView是否被重用,被重用run()方法也直接返回,为什么要用isViewReused()方法呢?主要是ListView,GridView我们会复用item对象,假如我们先去加载ListView,GridView第一页的图片的时候,第一页图片还没有全部加载完我们就快速的滚动,isViewReused()方法就会避免这些不可见的item去加载图片,而直接加载当前界面的图片.
在tryLoadBitmap()判断磁盘中是否有缓存,就开始进行网络下载(tryCacheImageOnDisk())。在tryCacheImageOnDisk()函数中有个tryCacheImageOnDisk()的 loaded
= downloadImage()这行进行图片下载。
private boolean tryCacheImageOnDisk() throws TaskCancelledException { L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey); boolean loaded; try { //进行下载图片,并保存在SD卡中 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; }
private boolean downloadImage() throws IOException { // 根据网络状态选择下载器 // SlowNetWorkDownloader 慢速 // downloader 正常速度 // networkDeniedDownloader 网络拒绝 InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader()); if (is == null) { L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey); return false; } else { try { //利用Disk CaChe将Bitmap写入磁盘缓存 return configuration.diskCache.save(uri, is, this); } finally { IoUtils.closeSilently(is); } } }
获取一个实现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进行图片的解析。
private Bitmap decodeImage(String imageUri) throws IOException { ViewScaleType viewScaleType = imageAware.getScaleType(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType, getDownloader(), options); return decoder.decode(decodingInfo); }
decode()函数最终是调用BaseImageDecoder.decode()方法进行解析的,这个利用之前获得的inputStream,直接从它身上读取数据,然后进行解析,并对整个下载任务的网络接口进行重置。
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { Bitmap decodedBitmap; ImageFileInfo imageInfo; InputStream imageStream = getImageStream(decodingInfo); if (imageStream == null) { L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey()); return null; } 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(...)函数中。
我们进去DisplayBitmapTask.run()函数中看看。除去前面几行的ImageLoadingListener.ImageLoadingListener()代码,相关代码其实就一行 displayer.display(bitmap,
imageAware, loadedFrom),它其实就是调用BitmapDisplayer这个对象将Bitmap对象显示到ImageView上。根据实现BitmapDisplayer接口的不同对象,还有SimpleBitmapDisplayer、FadeInBitmapDisplayer、RoundedBitmapDisplayer等对象.
public void run() { if (imageAware.isCollected()) { // ImageView已经被回收 L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey); listener.onLoadingCancelled(imageUri, imageAware.getWrappedView()); } else if (isViewWasReused()) { // ImageView已经被重用 L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey); listener.onLoadingCancelled(imageUri, imageAware.getWrappedView()); } else { L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey); displayer.display(bitmap, imageAware, loadedFrom); engine.cancelDisplayTaskFor(imageAware); listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap); } }最后可以再次看看上面的流程图。。。今天记录到此。。。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories