Universal-Image-Loader图片加载框架
来源:互联网 发布:linux 设置alias 编辑:程序博客网 时间:2024/05/17 08:59
引言
在前面的安卓面试系列–OOM异常(二)中我们已经给大家简单分析了一下Universal-Image-Loader,并且还在文末给大家提供了一个已经封装好的工具类,不知道大家觉得好不好用呢?今天我们就来分析一下安卓中几大主流的图片加载框架的优缺点。
Universal-Image-Loader
我们先来回顾一下这个图片加载框架,主要是要进行两个配置,一个是图片下载前的配置,还有一个是图片显示配置,我们一个一个的来说。
1、导包
方式一:
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
方式二:
导入jar包到libs目录下,jar包自行百度,这里就不提供了
2、权限配置
<!-- 如果想要加载网络图片就需要下面这个权限 --> <uses-permission android:name="android.permission.INTERNET" /><!-- 如果想要加载SD卡上的图片就需要下面这个权限 --> <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3、图片下载配置ImageLoaderConfiguration
a、使用默认配置:
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
b、自己配置参数:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) .memoryCacheExtraOptions(480, 800) // default = device screen dimensions 内存缓存文件的最大长宽 .diskCacheExtraOptions(480, 800, null) // 本地缓存的详细信息(缓存的最大长宽),最好不要设置这个 .taskExecutor(...) .taskExecutorForCachedImages(...) .threadPoolSize(3) // default 线程池内加载的数量 .threadPriority(Thread.NORM_PRIORITY - 2) // default 设置当前线程的优先级 .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通过自己的内存缓存实现 .memoryCacheSize(2 * 1024 * 1024) // 内存缓存的最大值 .memoryCacheSizePercentage(13) // default .diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定义缓存路径 .diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)缓存的最大值 .diskCacheFileCount(100) // 可以缓存的文件数量 // default为使用HASHCODE对UIL进行加密命名, 还可以用MD5(new Md5FileNameGenerator())加密 .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) .imageDownloader(new BaseImageDownloader(context)) // default .imageDecoder(new BaseImageDecoder()) // default .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default .writeDebugLogs() // 打印debug log .build(); //开始构建
配置好ImageLoaderConfiguration,一定不要忘记进行初始化操作(一般在application中进行初始化)
ImageLoader.getInstance().init(config);
注:上面的配置请根据自己的需要进行配置,不是所有的都要进行配置的
4、图片显示配置
a、首先要得到ImageLoader的实例(使用的单例模式)
ImageLoader imageLoader = ImageLoader.getInstance();
b、相关显示参数设置
DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_stub) // 设置图片下载期间显示的图片 .showImageForEmptyUri(R.drawable.ic_empty) // 设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.drawable.ic_error) // 设置图片加载或解码过程中发生错误显示的图片 .resetViewBeforeLoading(false) // default 设置图片在加载前是否重置、复位 .delayBeforeLoading(1000) // 下载前的延迟时间 .cacheInMemory(false) // default 设置下载的图片是否缓存在内存中 .cacheOnDisk(false) // default 设置下载的图片是否缓存在SD卡中 .preProcessor(...) .postProcessor(...) .extraForDownloader(...) .considerExifParams(false) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片缩放方式 .bitmapConfig(Bitmap.Config.ARGB_8888) // default 设置图片的解码类型 .decodingOptions(...) // 图片的解码设置 .displayer(new SimpleBitmapDisplayer()) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20) .handler(new Handler()) // default .build();
注:如果DisplayImageOption没有传递给ImageLoader.displayImage(…)方法,那么默认配置显示选项
(ImageLoaderConfiguration.defaultDisplayImageOptions(…))将被使用。
1).imageScaleType(ImageScaleType imageScaleType) //设置图片的缩放方式 缩放类型mageScaleType: EXACTLY :图像将完全按比例缩小的目标大小 EXACTLY_STRETCHED:图片会缩放到目标大小完全 IN_SAMPLE_INT:图像将被二次采样的整数倍 IN_SAMPLE_POWER_OF_2:图片将降低2倍,直到下一减少步骤,使图像更小的目标大小 NONE:图片不会调整 2).displayer(BitmapDisplayer displayer) //设置图片的显示方式 显示方式displayer: RoundedBitmapDisplayer(int roundPixels)设置圆角图片 FakeBitmapDisplayer()这个类什么都没做 FadeInBitmapDisplayer(int durationMillis)设置图片渐显的时间 SimpleBitmapDisplayer()正常显示一张图片
c、显示图片:
1、 ImageLoader.getInstance().displayImage(uri, imageView); 2、 ImageLoader.getInstance().displayImage(uri, imageView, options); 3、 ImageLoader.getInstance().displayImage(uri, imageView, listener); 4、 ImageLoader.getInstance().displayImage(uri, imageView, options, listener); 5、 ImageLoader.getInstance().displayImage(uri, imageView, options, listener, progressListener);
其中:
imageUrl 图片的URL地址
imageView 显示图片的ImageView控件
options DisplayImageOptions配置信息
listener 图片下载情况的监听
progressListener 图片下载进度的监听
- 方法1:最简单的方式,我们只需要定义要显示的图片的URL和要显示图片的ImageView。这种情况下,图片的显示选项会使用默认的配置
- 方法2:加载自定义配置的一个图片
- 方法3:加载带监听的一个图片
- 方法4:加载自定义配置且带监听的一个图片
参数最全的方法:
ImageLoader.getInstance().displayImage(uri, imageView, options, new ImageLoadingListener() { @Override public void onLoadingStarted(String arg0, View arg1) { //开始加载 } @Override public void onLoadingFailed(String arg0, View arg1, FailReason arg2) { //加载失败 } @Override public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) { //加载成功 } @Override public void onLoadingCancelled(String arg0, View arg1) { //加载取消 } }, new ImageLoadingProgressListener() { @Override public void onProgressUpdate(String imageUri, View view, int current, int total) { //加载进度 } });
5、注意事项:
a、如果你的程序经常出现OOM,你可以尝试以下设置:
- 禁用在内存中缓存cacheInMemory(false);
- 减少配置的线程池的大小(.threadPoolSize(…)),建议1~5;
- 在显示选项中使用 .bitmapConfig(Bitmap.Config.RGB_565) . RGB_565模式消耗的内存比ARGB_8888模式少两倍;
- 配置中使用.diskCacheExtraOptions(480, 320, null);
- 配置中使用 .memoryCache(newWeakMemoryCache()) 或者完全禁用在内存中缓存(don’t call .cacheInMemory());
- 在显示选项中使用.imageScaleType(ImageScaleType.EXACTLY) 或 .imageScaleType(ImageScaleType.IN_SAMPLE_INT);
b、一定要对ImageLoaderConfiguration进行初始化,否则会报错;
c、缓存到Sd卡需要在AndroidManifest.xml文件中进行如下配置:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
d、内存缓存可以使用以下已经已实现的方法(ImageLoaderConfiguration.memoryCache(…)):
- 只使用强引用:
LruMemoryCache (缓存大小超过指定值时,删除最近最少使用的bitmap) --默认情况下使用
- 缓存使用弱引用和强引用:
UsingFreqLimitedMemoryCache (缓存大小超过指定值时,删除最少使的bitmap) LRULimitedMemoryCache (缓存大小超过指定值时,删除最近最少使用的<span style="font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">bitmap) --默认值</span> FIFOLimitedMemoryCache (缓存大小超过指定值时,按先进先出规则删除的<span style="font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">bitmap)</span> LargestLimitedMemoryCache (缓存大小超过指定值时,删除最大的bitmap) LimitedAgeMemoryCache (缓存对象超过定义的时间后删除)
- 只使用弱引用:
WeakMemoryCache(没有限制缓存)
e、硬盘缓存可以使用以下已经实现的方式(ImageLoaderConfiguration.diskCache(…))
UnlimitedDiskCache 不限制缓存大小(默认) TotalSizeLimitedDiskCache (设置总缓存大小,超过时删除最久之前的缓存) FileCountLimitedDiskCache (设置总缓存文件数量,当到达警戒值时,删除最久之前的缓存。如果文件的大小都一样的时候,可以使用该模式) LimitedAgeDiskCache (不限制缓存大小,但是设置缓存时间,到期后删除)
6、总结
- 好了,到这里我们已经对UIL这个框架的基本使用的介绍已经结束了,简单总结一下,使用这个框架首先需要导包,配置权限,配置图片下载前的各项参数,配置图片显示的各项参数,然后就是通过ImageLoader实例对象的displayImage()方法展示图片。
- 最简单的就是传入一个url和一个ImageView控件,当然也可以添加自定义图片显示配置options,图片下载情况监听listener,主要监听图片下载开始、完成、失败、取消四种状态,图片下载进度监听progressListener。
- 然后我们还提供了几种内存缓存和硬盘缓存策略,先说内存缓存,默认的就是LruMemoryCache(强引用),当内存满了以后,删除最近最少使用的图片,还有一些其他的删除规则,比如时间先后,文件大小等等。再说硬盘缓存,跟内存缓存类似,也可以按照时间先后来删除,还可以设定缓存时间,到期删除。
你以为UIL到这里就结束了?作为一个有追求的程序员,怎么可以不看源码。
UIL图片框架缓存策略
关于UIL的三级缓存大家可以去看看我的这篇博客安卓面试系列–OOM异常(二),里面介绍了内存缓存、硬盘缓存以及网络下载三种缓存方式的调用顺序,这里就不多赘述了。在这里我想给大家讲一讲UIL框架中最著名的LruCache(最近最少使用)算法是怎么实现的。
点开UIL框架的源码,我们可以看到这样的一个目录结构:
其中:memory表示内存缓存目录,disk表示硬盘缓存目录
Lru缓存策略
我们先来讲一下内存缓存,首先分析一下LruMemoryCache这个类,这个类实现了一个接口MemoryCache,所以我们先来看下这个接口里面有什么方法:
/*** 内存缓存的接口*/public interface MemoryCache { /** * 根据键把图片加入内存 */ boolean put(String key, Bitmap value); /** * 根据键从内存中取出图片 */ Bitmap get(String key); /** * 根据键从内存中移除图片 */ Bitmap remove(String key); /** * 返回所有的键 */ Collection<String> keys(); /** * 清空所有数据 */ void clear();}
/*** LRU 缓存策略 Least Recentlty Use 最近最少使用*/public class LruMemoryCache implements MemoryCache{ /** * 链表数据结构存储 */ private final LinkedHashMap<String, Bitmap> map; /** * 最大缓存大小 */ private final int maxSize; /** * 当前使用的缓存大小 */ private int size; /** * 构造方法初始化缓存类 */ public LruMemoryCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);//ture,表示它是按照访问顺序进行排序的 } /** * 取出当前key对应的图片,并把这张图片放在list的尾部 */ @Override public final Bitmap get(String key) { if (key == null) { throw new NullPointerException("key == null"); } synchronized (this) { return map.get(key); } } /** * 添加图片,添加至末尾 */ @Override public final boolean put(String key, Bitmap value) { if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } synchronized (this) { //加入新图片的大小 size += sizeOf(key, value); // 图片已经存在 ,替换原来的图片, Bitmap previous = map.put(key, value); if (previous != null) { //移除旧图片大小 size -= sizeOf(key, previous); } } trimToSize(maxSize); return true; } /** * 超过maxSize,不停的移除图片,直到小于maxSize */ private void trimToSize(int maxSize) { // 循环判断当前缓存大小 while (true) { String key; Bitmap value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize || map.isEmpty()) { break; } // 链表结构最少使用的数据项 Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next(); if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= sizeOf(key, value); } } } /** * 移除图片 */ @Override public final Bitmap remove(String key) { if (key == null) { throw new NullPointerException("key == null"); } synchronized (this) { Bitmap previous = map.remove(key); if (previous != null) { size -= sizeOf(key, previous); } return previous; } } @Override public Collection<String> keys() { synchronized (this) { return new HashSet<String>(map.keySet()); } } /** * 清空数据 */ @Override public void clear() { trimToSize(-1); } /** * 计算Bitmap的大小 */ private int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override public synchronized final String toString() { return String.format("LruCache[maxSize=%d]", maxSize); }}
总结
- 以上就是内存缓存最近最少使用算法的实现,总结一下,把一张图片加入到内存缓存中首先要判断键和值是否为空,如果为空就抛出异常,都不为空的情况下,我们使用一个同步块,先计算一下新加入内存的图片有多大,然后把它加入内存,再判断一下这张图片是否已经存在于内存,如果存在就把这张图片删除,更新一下内存。假如我们新添加的图片非常大,超过了我们的内存大小,这个时候就有必要开始尝试移除图片的工作。移除图片的工作主要在trimToSize(maxSize)这个方法中进行,主要通过一个while循环,判断,如果内存没有超过设定的最大值,那么就不需要删除任何对象,直接break。如果超过了,我们就通过map集合的迭代器取出第一个数据项,这个数据项就是最近最少使用的那一项,然后把它移除,再更新一下内存,再次判断,知道内存小于设定的最大值为止。这样我们就实现了最近最少使用算法。
先进先出策略
再给大家将一个先进先出的策略吧,直接上源码:
public class FIFOLimitedMemoryCache extends LimitedMemoryCache { private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>()); public FIFOLimitedMemoryCache(int sizeLimit) { super(sizeLimit); } @Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { queue.add(value); //添加到队列的最后 return true; } else { return false; } } @Override public Bitmap remove(String key) { Bitmap value = super.get(key); if (value != null) { queue.remove(value); } return super.remove(key); } @Override public void clear() { queue.clear(); super.clear(); } @Override protected int getSize(Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override protected Bitmap removeNext() { return queue.remove(0); //移除第一个 } @Override protected Reference<Bitmap> createReference(Bitmap value) { return new WeakReference<Bitmap>(value); }}
- 可以看到,我们添加的时候是添加到queue的末尾,而移除的时候是移除第一个,这样就实现了FIFO(先进先出)算法。
- universal-image-Loader网络图片加载框架
- 使用Universal-Image-Loader框架加载图片
- Universal-Image-Loader图片加载框架
- 图片异步加载框架 Android-Universal-Image-Loader
- Android-Universal-Image-Loader 图片异步加载框架
- 图片异步加载框架 Android-Universal-Image-Loader
- 图片异步加载框架 Android-Universal-Image-Loader
- Android-Universal-Image-Loader异步加载图片框架学习研究
- 图片异步加载框架 Android-Universal-Image-Loader
- Android-Universal-Image-Loader异步加载图片框架学习研究
- Android异步加载图片开源框架Universal-Image-Loader
- Android-Universal-Image-Loader异步加载图片框架学习研究
- 异步加载图片框架Android-Universal-Image-Loader的使用
- Android图片异步加载框架Android-Universal-Image-Loader
- Android图片异步加载框架Android-Universal-Image-Loader
- Android图片异步加载框架Android-Universal-Image-Loader
- Android图片异步加载框架Android-Universal-Image-Loader
- Android-Universal-Image-Loader图片加载框架使用
- C++ 两个包含类互相调用彼此的类成员变量和方法
- Installing FLTK with Visual Studio
- 最小白_mysql的安装
- 安装vue开发调试神器vue-devtools
- 英语月总结
- Universal-Image-Loader图片加载框架
- moveToThread的最简单用法(依葫芦画瓢即可)(使得线程也更偏向于信号槽的使用方法)
- springmvc 本地jsp发送ajax请求 前后端合体
- 使用Gitolite搭建轻量级的Git服务器
- 70. Climbing Stairs
- Ubuntu 推荐 Mysql 可视化管理工具
- tomcat http转https
- Android之设置控件的阴影
- 设计模式(二)观察者模式(Head First)