Universal-Image-Loader分析:

Android-Universal-Image-Loader是一个开源的UI组件程序,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。
权限:

<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 


ImageLoaderConfiguration是针对图片缓存的全局配置,主要有线程类、缓存大小、磁盘大小、图片下载与解析、日志方面的配置

ImageLoader是具体下载图片,缓存图片,显示图片的具体执行类,它有两个具体的方法displayImage(...)、loadImage(...),但是其实最终他们的实现都是displayImage(...)。

DisplayImageOptions用于指导每一个Imageloader根据网络图片的状态(空白、下载错误、正在下载)显示对应的图片,是否将缓存加载到磁盘上,下载完后对图片进行怎么样的处理。

public class ImageLoadUtil {
private static ImageLoadUtil imageLoadUtil = null;
private ImageLoaderConfiguration config = null;
private DisplayImageOptions options = null;
private ImageLoadUtil(Context context){
options = new DisplayImageOptions.Builder() 
//.showImageOnLoading(R.drawable.ic_launcher) //设置图片在下载期间显示的图片 
//.showImageForEmptyUri(R.drawable.ic_launcher)//设置图片Uri为空或是错误的时候显示的图片 
//.showImageOnFail(R.drawable.ic_launcher) //设置图片加载/解码过程中错误时候显示的图片
.cacheInMemory(true)//设置下载的图片是否缓存在内存中 
.cacheOnDisc(true)//设置下载的图片是否缓存在SD卡中 
.considerExifParams(true) //是否考虑JPEG图像EXIF参数(旋转,翻转)
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)//设置图片以如何的编码方式显示 
.bitmapConfig(Bitmap.Config.RGB_565)//设置图片的解码类型// 
//.delayBeforeLoading(int delayInMillis)//int delayInMillis为你设置的下载前的延迟时间
//设置图片加入缓存前,对bitmap进行设置 
//.preProcessor(BitmapProcessor preProcessor) 
.resetViewBeforeLoading(true)//设置图片在下载前是否重置,复位 
.displayer(new RoundedBitmapDisplayer(20))//是否设置为圆角,弧度为多少 
.displayer(new FadeInBitmapDisplayer(100))//是否图片加载好后渐入的动画时间 
.build();//构建完成

config = new ImageLoaderConfiguration.Builder(
context).threadPriority(Thread.NORM_PRIORITY - 2)// 加载图片的线程数
.denyCacheImageMultipleSizesInMemory() // 解码图像的大尺寸将在内存中缓存先前解码图像的小尺寸。
.discCacheFileNameGenerator(new Md5FileNameGenerator())// 设置磁盘缓存文件名称
.tasksProcessingOrder(QueueProcessingType.LIFO)// 设置加载显示图片队列进程
.writeDebugLogs() // Remove for release app
.discCache(new UnlimitedDiscCache(new File(FileCacheUtil.getPicCacheDir()))) // 文件缓存目录
.defaultDisplayImageOptions(options)// 创建配置过得DisplayImageOption对象 
.build(); 
/*config = new ImageLoaderConfiguration.Builder(context)
.threadPriority(Thread.NORM_PRIORITY - 2)
.denyCacheImageMultipleSizesInMemory()
.diskCacheFileNameGenerator(new Md5FileNameGenerator())
.tasksProcessingOrder(QueueProcessingType.LIFO)
.writeDebugLogs() 
.build();*/
ImageLoader.getInstance().init(config);
}

public static ImageLoadUtil init(Context context){
if (imageLoadUtil == null) {
imageLoadUtil = new ImageLoadUtil(context);
}
return imageLoadUtil;
}
}

 

首先分析:ImageLoaderConfiguration

1
因为使用时使用到ImageLoaderConfiguration的构造方法,所以先看ImageLoaderConfiguration的构造方法:

//设置内存缓存的选项,用于将图片将图片解析成最大指定宽高的大小的图片进行缓存(缓存在内存中的图片宽高)。
memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache)

//设置磁盘缓存的选项,maxImageWidthForDiskCache()和maxImageHeightForDiskCache()缓存在硬盘里面图片最大尺寸。

diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache, BitmapProcessor processorForDiskCache)

//设置自定义加载和显示任务的线程池
taskExecutor(Executor executor)

//设置自定义显示任务的线程池
taskExecutorForCachedImages(Executor executorForCachedImages)

//设置图片显示任务的线程池大小
threadPoolSize(int threadPoolSize)

//设置线程的优先级
threadPriority(int threadPriority)

//设置拒绝缓存一张图片的多个尺寸
denyCacheImageMultipleSizesInMemory()

//设置加载和显示图片任务队列的类型
tasksProcessingOrder(QueueProcessingType tasksProcessingType)

//设置最大内存缓存字节数
memoryCacheSize(int memoryCacheSize)

//设置最大内存缓存所占app可用内存的百分比
memoryCacheSizePercentage(int availableMemoryPercent)

//设置内存缓存算法
memoryCache(MemoryCache memoryCache)

//设置最大磁盘缓存字节数
diskCacheSize(int maxCacheSize)

//设置在磁盘缓存文件夹下最多文件数
diskCacheFileCount(int maxFileCount)

//设置磁盘缓存文件生成的命名规则
diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator)

//设置磁盘缓存
diskCache(DiskCache diskCache)

//设置下载图片的工具
imageDownloader(ImageDownloader imageDownloader)

//设置decode出bitmap的工具
imageDecoder(ImageDecoder imageDecoder)

//设置默认的DisplayImageOptions
defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions)

//log信息
writeDebugLogs()

public ImageLoaderConfiguration build() {
initEmptyFieldsWithDefaultValues();
return new ImageLoaderConfiguration(this);
}

在分析DisplayImageOptions(图片加载过程中各种情况的处理):

//设置图片加载过程中显示的图片
showImageOnLoading(int imageRes)

//设置空URI显示的图片
showImageForEmptyUri(int imageRes)

//设置发生错误显示的图片
showImageOnFail(int imageRes)

//设置图片加载前是否重置
resetViewBeforeLoading(boolean resetViewBeforeLoading)

//设置是否将图片缓存在内存里
cacheInMemory(boolean cacheInMemory)

//设置是否将图片缓存在磁盘上
cacheOnDisk(boolean cacheOnDisk)

//设置图片缩放的类型
imageScaleType(ImageScaleType imageScaleType)

//设置Bitmap.Config
bitmapConfig(Bitmap.Config bitmapConfig)

//设置图片解码的选项
decodingOptions(Options decodingOptions)

//设置加载任务前的延迟
delayBeforeLoading(int delayInMillis)

//设置下载时额外的对象
extraForDownloader(Object extra)

//设置是否考虑EXIF信息
considerExifParams(boolean considerExifParams)

//设置内存缓存bitmap对象前的处理
preProcessor(BitmapProcessor preProcessor)

//设置内存缓存bitmap对象后的处理
postProcessor(BitmapProcessor postProcessor)

//设置图片的显示方式
displayer(BitmapDisplayer displayer)

//true是直接调用LoadAndDisplayImageTask对象的run方法,false是放到线程池里面执行,默认false
syncLoading(boolean isSyncLoading)

//设置显示图片和触发ImageLoadingListener事件的自定义Handler对象
handler(Handler handler)

 

ImageLoader使用的是单利模式:

在展示的时候调用的是:displayImage和loadImage两个方法:
分析displayImage:

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
//检查UIL的配置是否被初始化
checkConfiguration();
if (imageAware == null) {
throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
}
if (listener == null) {
listener = emptyListener;
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}

if (TextUtils.isEmpty(uri)) {
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//没有图片时,显示displayImage传过来的图片不存在情况下的图片
if (options.shouldShowImageForEmptyUri()) {
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
imageAware.setImageDrawable(null);
}
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}

 

//计算Bitmap的大小,以便后面解析图片时用

ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
//内存缓存的名称
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);

listener.onLoadingStarted(uri, imageAware.getWrappedView());
//Bitmap是否缓存在内存?
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);

if (options.shouldPostProcess()) {
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
//处理并显示图片
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
} else {
//显示图片
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
} else {
//根据状态显示正在加载还是加载前的情况
if (options.shouldShowImageOnLoading()) {
imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
} else if (options.isResetViewBeforeLoading()) {
imageAware.setImageDrawable(null);
}

ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
//启动一个线程,加载并显示图片
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}

 

再往下分析LoadAndDisplayImageTask
根据uri看看磁盘中是不是已经缓存了这个文件,如果有了,调用decodeImage方法,将图片文件decode成bitmap对象,若文件不存在,调用tryCacheImageOnDisk()方法去下载并缓存图片到本地磁盘,再通过decodeImage方法将图片文件decode成bitmap对象

private Bitmap tryLoadBitmap() throws TaskCancelledException {
Bitmap bitmap = null;
try {
//尝试从磁盘缓存中读取Bitmap
File imageFile = configuration.diskCache.get(uri);
//如果内存中有该图片,加载
if (imageFile != null && imageFile.exists()) {
L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
loadedFrom = LoadedFrom.DISC_CACHE;

checkTaskNotActual();
bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
}
//没有缓存在磁盘,从网络中下载图片
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
loadedFrom = LoadedFrom.NETWORK;

String imageUriForDecoding = uri;
//将网络里面的图片缓存到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;
}

tryCacheImageOnDisk()里面是:

loaded = downloadImage();

private boolean downloadImage() throws IOException {
//获取图片输入流
InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
if (is == null) {
L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
return false;
} else {
try {
//不为null,进行SD卡保存
return configuration.diskCache.save(uri, is, this);
} finally {
IoUtils.closeSilently(is);
}
}
}

 

configuration.diskCache.save(uri, is, this),接下来看save保存图片到SD卡的程序;它先是生成一个后缀名.tmp的临时文件,通过downloader得到的输入流imageStream拷贝到OutputStream中, finally中将临时文件tmpFile重命名回imageFile,并将tmpFile删除掉, 如果这些实现都没出什么问题,就reutrn一个true, 告诉别人,我save成功了:

public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
File imageFile = getFile(imageUri);
//tmp的临时文件
File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
boolean loaded = false;
try {
OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
try {
loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
} finally {
//将临时文件tmpFile重命名回imageFile,并将tmpFile删除掉
IoUtils.closeSilently(os);
}
} finally {
if (loaded && !tmpFile.renameTo(imageFile)) {
loaded = false;
}
if (!loaded) {
tmpFile.delete();
}
}
//true save成功
//false save失败
return loaded;
}

 

http://www.cnblogs.com/kissazi2/p/3901369.html

posted @ 2015-01-21 23:33  西北野狼  阅读(383)  评论(0编辑  收藏  举报