您的位置:首页 > 移动开发 > Android开发

Android 开源框架Universal-Image-Loader完全解析(三)---源代码解读

2015-10-14 10:15 465 查看


[java]
view plaincopyprint?





ImageView mImageView = (ImageView) findViewById(R.id.image);    
        String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg";    
            
        //显示图片的配置    
        DisplayImageOptions options = new DisplayImageOptions.Builder()    
                .showImageOnLoading(R.drawable.ic_stub)    
                .showImageOnFail(R.drawable.ic_error)    
                .cacheInMemory(true)    
                .cacheOnDisk(true)    
                .bitmapConfig(Bitmap.Config.RGB_565)    
                .build();    
            
        ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);    

ImageView mImageView = (ImageView) findViewById(R.id.image);
String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg";

//显示图片的配置
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub)
.showImageOnFail(R.drawable.ic_error)
.cacheInMemory(true)
.cacheOnDisk(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();

ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);
大部分的时候我们都是使用上面的代码去加载图片,我们先看下

[java]
view plaincopyprint?





public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {  
        displayImage(uri, new ImageViewAware(imageView), options, null, null);  
    }  

public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
displayImage(uri, new ImageViewAware(imageView), options, null, null);
}
从上面的代码中,我们可以看出,它会将ImageView转换成ImageViewAware, ImageViewAware主要是做什么的呢?该类主要是将ImageView进行一个包装,将ImageView的强引用变成弱引用,当内存不足的时候,可以更好的回收ImageView对象,还有就是获取ImageView的宽度和高度。这使得我们可以根据ImageView的宽高去对图片进行一个裁剪,减少内存的使用。

接下来看具体的displayImage方法啦,由于这个方法代码量蛮多的,所以这里我分开来读

[java]
view plaincopyprint?





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;  
        }  

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;
}
第1行代码是检查ImageLoaderConfiguration是否初始化,这个初始化是在Application中进行的

12-21行主要是针对url为空的时候做的处理,第13行代码中,ImageLoaderEngine中存在一个HashMap,用来记录正在加载的任务,加载图片的时候会将ImageView的id和图片的url加上尺寸加入到HashMap中,加载完成之后会将其移除,然后将DisplayImageOptions的imageResForEmptyUri的图片设置给ImageView,最后回调给ImageLoadingListener接口告诉它这次任务完成了。

[java]
view plaincopyprint?





ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());  
    String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);  
    engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);  
  
    listener.onLoadingStarted(uri, imageAware.getWrappedView());  
  
    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);  
        }  
    }  

ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);

listener.onLoadingStarted(uri, imageAware.getWrappedView());

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);
}
}
第1行主要是将ImageView的宽高封装成ImageSize对象,如果获取ImageView的宽高为0,就会使用手机屏幕的宽高作为ImageView的宽高,我们在使用ListView,GridView去加载图片的时候,第一页获取宽度是0,所以第一页使用的手机的屏幕宽高,后面的获取的都是控件本身的大小了

第7行从内存缓存中获取Bitmap对象,我们可以再ImageLoaderConfiguration中配置内存缓存逻辑,默认使用的是LruMemoryCache,这个类我在前面的文章中讲过

第11行中有一个判断,我们如果在DisplayImageOptions中设置了postProcessor就进入true逻辑,不过默认postProcessor是为null的,BitmapProcessor接口主要是对Bitmap进行处理,这个框架并没有给出相对应的实现,如果我们有自己的需求的时候可以自己实现BitmapProcessor接口(比如将图片设置成圆形的)

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

[java]
view plaincopyprint?





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);  
            }  

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

接下来我们就看LoadAndDisplayImageTask的run(), 这个类还是蛮复杂的,我们还是一段一段的分析

[java]
view plaincopyprint?





if (waitIfPaused()) return;  
if (delayIfNeed()) return;  

if (waitIfPaused()) return;
if (delayIfNeed()) return;


如果waitIfPaused(), delayIfNeed()返回true的话,直接从run()方法中返回了,不执行下面的逻辑, 接下来我们先看看waitIfPaused()

[java]
view plaincopyprint?





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);  
            }  
        }  
    }  
    return isTaskNotActual();  
}  

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);
}
}
}
return isTaskNotActual();
}

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

除此之外,这个方法的返回值由isTaskNotActual()决定,我们接着看看isTaskNotActual()的源码

[java]
view plaincopyprint?





private boolean isTaskNotActual() {  
        return isViewCollected() || isViewReused();  
    }  

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

[java]
view plaincopyprint?





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();  
  
            bmp = configuration.memoryCache.get(memoryCacheKey);  
            if (bmp == null || bmp.isRecycled()) {  
                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();  
        }  

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();

bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {
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();
}
第1行代码有一个loadFromUriLock,这个是一个锁,获取锁的方法在ImageLoaderEngine类的getLockForUri()方法中

[java]
view plaincopyprint?





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

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的请求就会在第7行等待,等到这次图片加载完成之后,ReentrantLock就被释放,刚刚那些相同Url的请求就会继续执行第7行下面的代码
来到第12行,它们会先从内存缓存中获取一遍,如果内存缓存中没有在去执行下面的逻辑,所以ReentrantLock的作用就是避免这种情况下重复的去从网络上面请求图片。

第14行的方法tryLoadBitmap(),这个方法确实也有点长,我先告诉大家,这里面的逻辑是先从文件缓存中获取有没有Bitmap对象,如果没有在去从网络中获取,然后将bitmap保存在文件系统中,我们还是具体分析下

[java]
view plaincopyprint?





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()));  
            }  

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()));
}
先判断文件缓存中有没有该文件,如果有的话,直接去调用decodeImage()方法去解码图片,该方法里面调用BaseImageDecoder类的decode()方法,根据ImageView的宽高,ScaleType去裁剪图片,具体的代码我就不介绍了,大家自己去看看,我们接下往下看tryLoadBitmap()方法

[java]
view plaincopyprint?





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);  
            }  
        }  

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);
}
}
第1行表示从文件缓存中获取的Bitmap为null,或者宽高为0,就去网络上面获取Bitmap,来到第6行代码是否配置了DisplayImageOptions的isCacheOnDisk,表示是否需要将Bitmap对象保存在文件系统中,一般我们需要配置为true, 默认是false这个要注意下,然后就是执行tryCacheImageOnDisk()方法,去服务器上面拉取图片并保存在本地文件中

[java]
view plaincopyprint?





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);  
}  
  
/** @return <b>true</b> - if image was downloaded successfully; <b>false</b> - otherwise */  
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);  
}  

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);
}

/** @return <b>true</b> - if image was downloaded successfully; <b>false</b> - otherwise */
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);
}
第6行的downloadImage()方法是负责下载图片,并将其保持到文件缓存中,将下载保存Bitmap的进度回调到IoUtils.CopyListener接口的onBytesCopied(int current, int total)方法中,所以我们可以设置ImageLoadingProgressListener接口来获取图片下载保存的进度,这里保存在文件系统中的图片是原图

第16-17行,获取ImageLoaderConfiguration是否设置保存在文件系统中的图片大小,如果设置了maxImageWidthForDiskCache和maxImageHeightForDiskCache,会调用resizeAndSaveImage()方法对图片进行裁剪然后在替换之前的原图,保存裁剪后的图片到文件系统的,之前有同学问过我说这个框架保存在文件系统的图片都是原图,怎么才能保存缩略图,只要在Application中实例化ImageLoaderConfiguration的时候设置maxImageWidthForDiskCache和maxImageHeightForDiskCache就行了

[java]
view plaincopyprint?





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);  
                }  

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);
}
接下来这里就简单了,6-12行是否要对Bitmap进行处理,这个需要自行实现,14-17就是将图片保存到内存缓存中去

[java]
view plaincopyprint?





DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);  
        runTask(displayBitmapTask, syncLoading, handler, engine);  

DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);
最后这两行代码就是一个显示任务,直接看DisplayBitmapTask类的run()方法

[java]
view plaincopyprint?





@Override  
    public void run() {  
        if (imageAware.isCollected()) {  
            L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);  
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());  
        } else if (isViewWasReused()) {  
            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);  
        }  
    }  

@Override
public void run() {
if (imageAware.isCollected()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else if (isViewWasReused()) {
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);
}
}

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

转载自http://blog.csdn.net/xiaanming/article/details/39057201
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息