ImageLoader深入源码学习探究


由于我当前的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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值