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