ImageLoader深入源码学习探究

来源:互联网 发布:debian ubuntu 哪个好 编辑:程序博客网 时间:2024/05/17 23:45

由于我当前的ImageLoader版本与读者们的版本可能不同,所以下面讲解的地方可能存在一些出入,但大体上的实现基本一致,请读者自己参照自己的imageloader源码来分析


一般在使用ImageLoader的时候都需要进行一些配置 如下
//显示图片的配置    
        
DisplayImageOptions options = new DisplayImageOptions.Builder()                    .showImageOnLoading(R.drawable.default)                    .showImageOnFail(R.drawable.error)                    .cacheInMemory(true)                    .cacheOnDisk(true)                    .bitmapConfig(Bitmap.Config.RGB_565)                    .build();    
 
然后会调用displayImage方法来加载图片           
ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);   

我们看下一下displayImage的实现
 public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) {        this.displayImage(uri, (ImageAware)(new ImageViewAware(imageView)), options, listener);}
将imageView转化成ImageViewAware,ImageViewAware实现了ImageAware接口,我们来看一下ImageViewAware 中的方法


首先是构造方法
public ImageViewAware(ImageView imageView) {        this(imageView, true);    }    public ImageViewAware(ImageView imageView, boolean checkActualViewSize) {        this.imageViewRef = new WeakReference(imageView);        this.checkActualViewSize = checkActualViewSize;    }


可以看到在第一个构造方法中调用了第二个构造方法,在第二个构造方法中使用了WeakReference,即将我们的imageView由强引用转化为弱引用,这样当内存不足的时候,可以更好的回收ImageView对象


接下来看一下displayImage的具体实现代码↓

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener) {        this.checkConfiguration();        if(imageAware == null) {            throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)");        } else {            if(listener == null) {                listener = this.emptyListener;            }            if(options == null) {                options = this.configuration.defaultDisplayImageOptions;            }            if(TextUtils.isEmpty(uri)) {                this.engine.cancelDisplayTaskFor(imageAware);                listener.onLoadingStarted(uri, imageAware.getWrappedView());                if(options.shouldShowImageForEmptyUri()) {                    imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources));                } else {                    imageAware.setImageDrawable((Drawable)null);                }                listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null);            } else {                ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());                String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);                this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);                listener.onLoadingStarted(uri, imageAware.getWrappedView());                Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);                ImageLoadingInfo imageLoadingInfo;                if(bmp != null && !bmp.isRecycled()) {                    if(this.configuration.writeLogs) {                        L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey});                    }                    if(options.shouldPostProcess()) {                        imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));                        ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());                        if(options.isSyncLoading()) {                            displayTask1.run();                        } else {                            this.engine.submit(displayTask1);                        }                    } else {                        bmp = options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);                        listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);                    }                } else {                    if(options.shouldShowImageOnLoading()) {                        imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));                    } else if(options.isResetViewBeforeLoading()) {                        imageAware.setImageDrawable((Drawable)null);                    }                    imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));                    LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());                    if(options.isSyncLoading()) {                        displayTask.run();                    } else {                        this.engine.submit(displayTask);                    }                }            }        }    }



在displayImage方法具体的实现中,第一步调用了checkConfiguration()方法

private void checkConfiguration() {        if(this.configuration == null) {            throw new IllegalStateException("ImageLoader must be init with configuration before using");        }    }


当我们的配置是空时,则会抛出异常ImageLoader must be init with configuration before using,这个异常在新手使用时比较容易遇到,这是因为没有init我们的imageloader,下面是初始化的代码(下面这段初始化的代码只提供参考,可根据实际情况自己配置自己需要的参数)
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context).threadPriority(Thread.NORM_PRIORITY - 2).denyCacheImageMultipleSizesInMemory().discCacheFileNameGenerator(new Md5FileNameGenerator()).tasksProcessingOrder(QueueProcessingType.LIFO).build();ImageLoader.getInstance().init(config);


我们再来分析一下displayImage中的这段代码

 if(TextUtils.isEmpty(uri)) {                this.engine.cancelDisplayTaskFor(imageAware);                listener.onLoadingStarted(uri, imageAware.getWrappedView());                if(options.shouldShowImageForEmptyUri()) {                    imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources));                } else {                    imageAware.setImageDrawable((Drawable)null);                }                listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null);            } else {....}

在if语句中,处理的就是当我们传递进去的url为空的情况,我们看到this.engine.cancelDisplayTaskFor(imageAware);有这么一句,那么这一句是什么意思呢?

engine是一个ImageLoaderEngine对象,ImageLoaderEngine中存在一个HashMap,用来记录正在加载的任务,加载图片的时候会将ImageView的id和图片的url加上尺寸加入到HashMap中,加载完成之后会将其移除,我们可以看cancelDisplayTaskFor的具体试下,他将正在加载中的任务的当前iamgeAware给remove掉了
  
 void cancelDisplayTaskFor(ImageAware imageAware) {        this.cacheKeysForImageAwares.remove(Integer.valueOf(imageAware.getId()));    }

然后将DisplayImageOptions的imageResForEmptyUri的图片设置给ImageView,最后回调给ImageLoadingListener接口告诉它这次任务完成了。


接下来我们就来分析一下在url不为空的情况下,这才是我们应该着重关注的部分


if(TextUtils.isEmpty(uri)) {                ...            } else {                ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());                String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);                this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);                listener.onLoadingStarted(uri, imageAware.getWrappedView());                Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);                ImageLoadingInfo imageLoadingInfo;                if(bmp != null && !bmp.isRecycled()) {                    if(this.configuration.writeLogs) {                        L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey});                    }                    if(options.shouldPostProcess()) {                        imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));                        ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());                        if(options.isSyncLoading()) {                            displayTask1.run();                        } else {                            this.engine.submit(displayTask1);                        }                    } else {                        bmp = options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);                        listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);                    }                } else {                    if(options.shouldShowImageOnLoading()) {                        imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));                    } else if(options.isResetViewBeforeLoading()) {                        imageAware.setImageDrawable((Drawable)null);                    }                    imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));                    LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());                    if(options.isSyncLoading()) {                        displayTask.run();                    } else {                        this.engine.submit(displayTask);                    }                }            }

首先它会调用ImageSizeUtils类的defineTargetSizeForView方法 将我们的imageAware封装为一个ImageSize对象 ,defineTargetSizeForView方法实现如下

 
 public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) {        int width = imageAware.getWidth();        if(width <= 0) {            width = maxImageSize.getWidth();        }        int height = imageAware.getHeight();        if(height <= 0) {            height = maxImageSize.getHeight();        }        return new ImageSize(width, height);    }

如果获取ImageView的宽高小于等于0,就会使用手机屏幕的宽高作为ImageView的宽高。

String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);

这一句的作用是生成一个缓存时使用的key,再从缓存中取数据的时候通过该key值来获取generateKey方法如下,非常简单,大家看看就好哈,这里就不说了↓

 public static String generateKey(String imageUri, ImageSize targetSize) {        return imageUri + "_" + targetSize.getWidth() + "x" + targetSize.getHeight();    }

this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);

这一句就是将当前任务加入到haspmap中记录起来,cacheKeysForImageAwares就是一个haspMap 如下↓

  void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) {        this.cacheKeysForImageAwares.put(Integer.valueOf(imageAware.getId()), memoryCacheKey);    }



Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);

这一句代码从内存缓存中获取Bitmap对象,我们可以再ImageLoaderConfiguration中配置内存缓存逻辑,默认使用的是LruMemoryCache。


我们再来看接下来的这一段代码

 if(options.shouldPostProcess()) {                        imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));                        ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler());                        if(options.isSyncLoading()) {                            displayTask1.run();                        } else {                            this.engine.submit(displayTask1);                        }                    } else {                        bmp = options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);                        listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);                    }

这一段代码是在if(bmp != null && !bmp.isRecycled())为true的情况下执行的,就是说是在缓存不为空且没有被回收的条件下执行的。我们如果在DisplayImageOptions中设置了postProcessor就进入true逻辑,不过默认postProcessor是为null的,BitmapProcessor接口主要是对Bitmap进行处理,这个框架并没有给出相对应的实现,如果我们有自己的需求的时候可以自己实现BitmapProcessor接口。


 bmp = options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);

这两行主要是将Bitmap设置到ImageView上面,这里我们可以在DisplayImageOptions中配置显示需求displayer,默认使用的是SimpleBitmapDisplayer,直接将Bitmap设置到ImageView上面,我们可以配置其他的显示逻辑, 他这里提供了FadeInBitmapDisplayer(透明度从0-1)RoundedBitmapDisplayer(4个角是圆弧)等, 然后回调ImageLoadingListener接口onLoadingComplete 加载完成。

 if(options.shouldShowImageOnLoading()) {                        imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));                    } else if(options.isResetViewBeforeLoading()) {                        imageAware.setImageDrawable((Drawable)null);                    }                    imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri));                    LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler());                    if(options.isSyncLoading()) {                        displayTask.run();                    } else {                        this.engine.submit(displayTask);                    }


这段代码主要是Bitmap不在内存缓存,从文件中或者网络里面获取bitmap对象,实例化一个LoadAndDisplayImageTask对象,LoadAndDisplayImageTask实现了Runnable,如果配置了isSyncLoading为true, 直接执行LoadAndDisplayImageTask的run方法,表示同步,默认是false,将LoadAndDisplayImageTask提交给线程池对象


我们来看一下LoadAndDisplayImageTask中的run方法如何实现的
 public void run() {        if(!this.waitIfPaused()) {            if(!this.delayIfNeed()) {               ...}        }    }

当waitIfPaused()和delayIfNeed()方法返回true时,会直接结束run方法,我们先来看看这两个方法的实现


waitIfPaused()方法
  
private boolean waitIfPaused() {        AtomicBoolean pause = this.engine.getPause();        synchronized(pause) {            if(pause.get()) {                this.log("ImageLoader is paused. Waiting...  [%s]");                try {                    pause.wait();                } catch (InterruptedException var5) {                    L.e("Task was interrupted [%s]", new Object[]{this.memoryCacheKey});                    return true;                }                this.log(".. Resume loading [%s]");            }        }        return this.checkTaskIsNotActual();    }


这个方法是干嘛用呢,主要是我们在使用ListView,GridView去加载图片的时候,有时候为了滑动更加的流畅,我们会选择手指在滑动或者猛地一滑动的时候不去加载图片,所以才提出了这么一个方法,那么要怎么用呢?  这里用到了PauseOnScrollListener这个类,使用很简单ListView.setOnScrollListener(new PauseOnScrollListener(pauseOnScroll, pauseOnFling )), pauseOnScroll控制我们缓慢滑动ListView,GridView是否停止加载图片,pauseOnFling 控制猛的滑动ListView,GridView是否停止加载图片
除此之外,这个方法的返回值由isTaskNotActual()决定,我们接着看看checkTaskIsNotActual()的源码

 
  private boolean checkTaskIsNotActual() {        return this.checkViewCollected() || this.checkViewReused();    }


checkViewCollected()是判断我们ImageView是否被垃圾回收器回收了,如果回收了,LoadAndDisplayImageTask方法的run()就直接返回了,checkViewReused()判断该ImageView是否被重用,被重用run()方法也直接返回,为什么要用checkViewReused()方法呢?主要是ListView,GridView我们会复用item对象,假如我们先去加载ListView,GridView第一页的图片的时候,第一页图片还没有全部加载完我们就快速的滚动,checkViewReused()方法就会避免这些不可见的item去加载图片,而直接加载当前界面的图片

delayIfNeed()方法与waitIfPaused() 一样,都是由checkTaskIsNotActual()来控制返回值,就不多说这个方法了。

然后我们来看看当这两个都返回false时,执行的代码

 ReentrantLock loadFromUriLock = this.imageLoadingInfo.loadFromUriLock;                this.log("Start display image task [%s]");                if(loadFromUriLock.isLocked()) {                    this.log("Image already is loading. Waiting... [%s]");                }                loadFromUriLock.lock();                Bitmap bmp;                try {                    if(this.checkTaskIsNotActual()) {                        return;                    }                    bmp = (Bitmap)this.configuration.memoryCache.get(this.memoryCacheKey);                    if(bmp == null) {                        bmp = this.tryLoadBitmap();                        if(this.imageAwareCollected) {                            return;                        }                        if(bmp == null) {                            return;                        }                        if(this.checkTaskIsNotActual() || this.checkTaskIsInterrupted()) {                            return;                        }                        if(this.options.shouldPreProcess()) {                            this.log("PreProcess image before caching in memory [%s]");                            bmp = this.options.getPreProcessor().process(bmp);                            if(bmp == null) {                                L.e("Pre-processor returned null [%s]", new Object[0]);                            }                        }                        if(bmp != null && this.options.isCacheInMemory()) {                            this.log("Cache image in memory [%s]");                            this.configuration.memoryCache.put(this.memoryCacheKey, bmp);                        }                    } else {                        this.loadedFrom = LoadedFrom.MEMORY_CACHE;                        this.log("...Get cached bitmap from memory after waiting. [%s]");                    }                    if(bmp != null && this.options.shouldPostProcess()) {                        this.log("PostProcess image before displaying [%s]");                        bmp = this.options.getPostProcessor().process(bmp);                        if(bmp == null) {                            L.e("Pre-processor returned null [%s]", new Object[]{this.memoryCacheKey});                        }                    }                } finally {                    loadFromUriLock.unlock();                }                if(!this.checkTaskIsNotActual() && !this.checkTaskIsInterrupted()) {                    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, this.imageLoadingInfo, this.engine, this.loadedFrom);                    displayBitmapTask.setLoggingEnabled(this.writeLogs);                    if(this.options.isSyncLoading()) {                        displayBitmapTask.run();                    } else {                        this.handler.post(displayBitmapTask);                    }                }



我们可以看到在第一行中有一个loadFromUriLock,这个其实就是一个锁,他可以通过ImageLoaderEngine类的getLockForUri()方法来获取


ReentrantLock getLockForUri(String uri) {          ReentrantLock lock = uriLocks.get(uri);          if (lock == null) {              lock = new ReentrantLock();              uriLocks.put(uri, lock);          }          return lock;      } 


这个锁对象与图片的url是相互对应的,为什么要这么做?不知道大家有没有考虑过一个场景,假如在一个ListView中,某个item正在获取图片的过程中,而此时我们将这个item滚出界面之后又将其滚进来,滚进来之后如果没有加锁,该item又会去加载一次图片,假设在很短的时间内滚动很频繁,那么就会出现多次去网络上面请求图片,所以这里根据图片的Url去对应一个ReentrantLock对象,让具有相同Url的请求就会在等待,等到这次图片加载完成之后,ReentrantLock就被释放,刚刚那些相同Url的请求才会继续执行下面的代码


接下来又会执行bmp = (Bitmap)this.configuration.memoryCache.get(this.memoryCacheKey);这一句代码,先从内存缓存中获取一遍,如果内存缓存中没有在去执行下面的逻辑,所以ReentrantLock的作用就是避免这种情况下重复的去从网络上面请求图片。


当内存中没有缓存该图片时  会执行一个tryLoadBitmap()方法,
private Bitmap tryLoadBitmap() {        File imageFile = this.getImageFileInDiscCache();        Bitmap bitmap = null;        try {            if(imageFile.exists()) {                this.log("Load image from disc cache [%s]");                this.loadedFrom = LoadedFrom.DISC_CACHE;                bitmap = this.decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));                if(this.imageAwareCollected) {                    return null;                }            }            if(bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {                this.log("Load image from network [%s]");                this.loadedFrom = LoadedFrom.NETWORK;                String e = this.options.isCacheOnDisc()?this.tryCacheImageOnDisc(imageFile):this.uri;                if(!this.checkTaskIsNotActual()) {                    bitmap = this.decodeImage(e);                    if(this.imageAwareCollected) {                        return null;                    }                    if(bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {                        this.fireFailEvent(FailType.DECODING_ERROR, (Throwable)null);                    }                }            }        } catch (IllegalStateException var4) {            this.fireFailEvent(FailType.NETWORK_DENIED, (Throwable)null);        } catch (IOException var5) {            L.e(var5);            this.fireFailEvent(FailType.IO_ERROR, var5);            if(imageFile.exists()) {                imageFile.delete();            }        } catch (OutOfMemoryError var6) {            L.e(var6);            this.fireFailEvent(FailType.OUT_OF_MEMORY, var6);        } catch (Throwable var7) {            L.e(var7);            this.fireFailEvent(FailType.UNKNOWN, var7);        }        return bitmap;    }


这里面的逻辑是先从文件缓存中获取有没有Bitmap对象,如果没有在去从网络中获取,然后将bitmap保存在文件系统中,我们来看一下它从网络获取图片后是如何进行缓存的

String e = this.options.isCacheOnDisc()?this.tryCacheImageOnDisc(imageFile):this.uri;

先检查是否配置了DisplayImageOptions的isCacheOnDisk,表示是否需要将Bitmap对象保存在文件系统中,一般我们需要配置为true,当为true时就会调用tryCacheImageOnDisc()这个方法了
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;  }  

private boolean downloadImage() throws IOException {      InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());      return configuration.diskCache.save(uri, is, this);  }


downloadImage()方法是负责下载图片,并将其保持到文件缓存中,将下载保存Bitmap的进度回调到IoUtils.CopyListener接口的onBytesCopied(int current, int total)方法中,所以我们可以设置ImageLoadingProgressListener接口来获取图片下载保存的进度,这里保存在文件系统中的图片是原图

 int width = configuration.maxImageWidthForDiskCache;    int height = configuration.maxImageHeightForDiskCache; 
 
获取ImageLoaderConfiguration是否设置保存在文件系统中的图片大小,如果设置了maxImageWidthForDiskCache和maxImageHeightForDiskCache,会调用resizeAndSaveImage()方法对图片进行裁剪然后在替换之前的原图,保存裁剪后的图片到文件系统的,所以我们只要在Application中实例化ImageLoaderConfiguration的时候设置maxImageWidthForDiskCache和maxImageHeightForDiskCache就可以保存缩略图了

然后我们再回到run方法中,执行完tryLoadBitmap()后会执行下面这段代码,将图片保存到内存缓存中去

if(bmp != null && this.options.isCacheInMemory()) {                            this.log("Cache image in memory [%s]");                            this.configuration.memoryCache.put(this.memoryCacheKey, bmp);                        }

最后这一段代码就是一个显示的过程
 if(!this.checkTaskIsNotActual() && !this.checkTaskIsInterrupted()) {                    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, this.imageLoadingInfo, this.engine, this.loadedFrom);                    displayBitmapTask.setLoggingEnabled(this.writeLogs);                    if(this.options.isSyncLoading()) {                        displayBitmapTask.run();                    } else {                        this.handler.post(displayBitmapTask);                    }                }

我们直接看一下displayBitmapTask.run();方法
 
public void run() {        if(this.imageAware.isCollected()) {            if(this.loggingEnabled) {                L.d("ImageAware was collected by GC. Task is cancelled. [%s]", new Object[]{this.memoryCacheKey});            }            this.listener.onLoadingCancelled(this.imageUri, this.imageAware.getWrappedView());        } else if(this.isViewWasReused()) {            if(this.loggingEnabled) {                L.d("ImageAware is reused for another image. Task is cancelled. [%s]", new Object[]{this.memoryCacheKey});            }            this.listener.onLoadingCancelled(this.imageUri, this.imageAware.getWrappedView());        } else {            if(this.loggingEnabled) {                L.d("Display image in ImageAware (loaded from %1$s) [%2$s]", new Object[]{this.loadedFrom, this.memoryCacheKey});            }            Bitmap displayedBitmap = this.displayer.display(this.bitmap, this.imageAware, this.loadedFrom);            this.listener.onLoadingComplete(this.imageUri, this.imageAware.getWrappedView(), displayedBitmap);            this.engine.cancelDisplayTaskFor(this.imageAware);        }    }

假如ImageView被回收了或者被重用了,就回调ImageLoadingListener接口的onLoadingCancelled方法,否则就调用BitmapDisplayer去显示Bitmap。


到此整个加载和缓存的过程就讲完了,里面有很多讲得不好的地方 欢迎大家一起讨论。


1 0
原创粉丝点击