本文转载于:http://www.cnblogs.com/osmondy/p/3266023.html
浅谈开源项目Android-Universal-Image-Loader(Part 3.1)
最近,在办离职手续,相对来说,比之前一年多忙碌的外包闲了许多。总算有时间去做些平时喜欢而没空去做的事情。一直觉得项目中使用的Image Loader适用性不强,昨晚在github随便逛逛,发现一个开源项目Android-Universal-Image-Loader十分火热。代码并不十分复杂,却写的不错,决定记录和分享一下。
Android-Universal-Image-Loader是一个针对图片加载、缓存的开源项目。github: https://github.com/nostra13/Android-Universal-Image-Loader
@author nostra13频繁的更新记录来看,Android-Universal-Image-Loader的后期维护比较好。从Applications using Universal Image Loader来看,还是相当强大的,如:EyeEm Camera, UPnP/DLNA Browser, Facebook Photo/Album,淘宝天猫,京东商城等。到底有何种魅力让大家纷至沓来?
01. 项目包结构
图 1
去掉无实际意义的包名:com.nostra13.universalimageloader,一切变显得比较清楚了。
(1)cache主要是磁盘缓存及内存缓存预定的接口和常规实现类,包含的算法较多(并不复杂),如FIFO算法、LRU算法等。
(2)core明显是整个Image Loader的核心包,图片下载、适配显示,并向上层应用提供各种接口,默认模板,还包括很多关键枚举类、工具类。
(3)utils比较简单些,常规工具类,如ImageSizeUtils、StorageUtils等。
02. 文件存储策略
针对手机,流量、电量及体验的要求,不断向网络发起重复的请求,都是不恰当的。常规存储方式包括:文件存储、数据库存储、SharedPreference、云存储(网络存储)及保存至内存等几种手段。显然,需要在本地进行持久化,就图片而已,以文件的方式保存无疑是最好的选择。至此,完成了云端-->本地的持久化,但是如果简单的每次都去”本地存储“请求,请求不到再到云端请求,无疑无法解决频繁请求图片所需要的速度和体验。请记住,用户对图片的渴望程序是相当高,试想一下,ListView上下滚动时,每次直接到”本地存储“请求时,整个界面多处在Loading,显然体验效果比较一般。因此,图片一般都会存储至内存中。从内存中读取图片的速度比本地加载的快得多,体验也会更好。这就是平时通常说的三步:内存-->本地文件-->云端。
在Android-Universal-Image-Loader,单纯从包结构上就可以看出,cache.disc是负责硬盘存储,而cache.memory是负责内存存储的(ps.具体存储实现方式还是在core包内)。而网络请求应该在core.download中,core包在Part3.2会深入分析。
事实上,我们似乎忽略了一些东西,如果我发起请求的是本地图片呢?如果是R.drawable.*的图片呢?优点1:Android-Universal-Image-Loader设计得比较细致,在ImageDownloader接口枚举类Scheme,明确指出所支持的请求类型:
HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
021. 存储空间、时间、数量策略
在cache.disc下,有着比较完整的存储策略,根据预先指定的空间大小,使用频率(生命周期),文件个数的约束条件,都有着对应的实现策略。最基础的接口DiscCacheAware和抽象类BaseDiscCache。简单类图如下:
图2
从类图中,明显可以看出,整个disc存储的实现方式有四种:文件总数,文件总大小(或目录),生命周期,无限制(即空间、时间维度的限制)。分析重点,是接口/抽象类。
public interface DiscCacheAware {
/**
* This method must not to save file on file system in fact. It is called after image was cached in cache directory
* and it was decoded to bitmap in memory. Such order is required to prevent possible deletion of file after it was
* cached on disc and before it was tried to decode to bitmap.
*/
void put(String key, File file); /**
* Returns {@linkplain File file object} appropriate incoming key.<br />
* <b>NOTE:</b> Must <b>not to return</b> a null. Method must return specific {@linkplain File file object} for
* incoming key whether file exists or not.
*/
File get(String key); /** Clears cache directory */
void clear();
}
具体看英文注释,比较好理解,此处就不翻译了。
022. 图片文件的命名
如果你是心思细腻的用户,在图片浏览器中是否会经常看到一些小图片甚至于广告图片。在一定程序上,本人挺鄙视这些图片来源的应用。为什么把一些临时下载的图片,并无实际意义的图片,占据如此重要的位置。对此,我只想说:我会直接删掉这个目录,如果知道是哪个应用的话,估计我也会卸载。我不禁猜想:如果所有应用缓存的图片,都是可以被媒体库扫描出来的话,这是什么境况呢?慢,肯定的。
因此,图片保存时,以什么样的文件名进行存储是相当重要的。这个必须要根据需求进行定制生成的策略。举三个例子:
(1)如果要做一个“云相册”,保存图片后,当然希望能够使用“美图秀秀”、“相机360”等进行编辑。
(2)如果要做一个“淘宝客户端”,所有浏览过的商品图片都存储,并可被其它图片浏览器感知,这似乎不恰当吧。
(3)如果要做一个“私密相册”,保存本地的图片,自然不希望别人去感知。
结论:用户有意识去保存的图片,应该可被感知;用户无意识缓存的图片,不应该被“外界”感知,但也有例外如第3种。当然,第三种权限的控制也是可行的。
Android-Universal-Image-Loader在保存图片时,需要一个FileNameGenerator(译:文件名生产者)。抽象类BaseDiscCache中存在两个构造器:
public BaseDiscCache(File cacheDir) {
this(cacheDir, DefaultConfigurationFactory.createFileNameGenerator());
} public BaseDiscCache(File cacheDir, FileNameGenerator fileNameGenerator) {
if (cacheDir == null) {
throw new IllegalArgumentException(String.format(ERROR_ARG_NULL, "cacheDir"));
}
if (fileNameGenerator == null) {
throw new IllegalArgumentException(String.format(ERROR_ARG_NULL, "fileNameGenerator"));
} this.cacheDir = cacheDir;
this.fileNameGenerator = fileNameGenerator;
}
DefaultConfigurationFactory提供默认文件名生产者为HashCodeFileNameGenerator,默认实现比较简单:返回字符串的hashCode。
核心类图:
图3
Md5FileNameGenerator相关原理可查询MD5加密算法,JDK自带类java.security.MessageDigest。
03. 内存存储
这部分涉及的知识面比较多,算法、链表、堆栈、队列、泛型等。Android-Universal-Image-Loader在“开闭原则”上做得很好,接口简洁、实用,基于接口,都可以自行扩展功能。
直接看下接口MemoryCacheAware的定义:
public interface MemoryCacheAware<K, V> {
/**
* Puts value into cache by key
*
* @return <b>true</b> - if value was put into cache successfully, <b>false</b> - if value was <b>not</b> put into
* cache
*/
boolean put(K key, V value); /** Returns value by key. If there is no value for key then null will be returned. */
V get(K key); /** Removes item by key */
void remove(K key); /** Returns all keys of cache */
Collection<K> keys(); /** Remove all items from cache */
void clear();
}
如果不熟悉泛型的话,建议复习一下,或者直接把MemoryCacheAware<K, V>当MemoryCacheAware<String, Bitmap>看,会方便些。
核心类图:
图4
大体结构上以文件存储比较相近,关键性的区别在于BaseMemoryCache子类使用的都是WeakReference(弱引用),FuzzyKeyMemoryCache、LimitedAgeMemoryCache、LruMemoryCache使用的是强引用。本节对内存存储方式,比较关键的概念:强引用、软引用、弱引用及虚引用。
回顾:
(1)StrongReference(强引用)
强引用就是平时经常使用的,如常规new Object()。如果一个对象具有强引用,那垃圾回收器绝不会回收。内存不足,甚至出现OOM时,也不会随意回收强引用的对象。
(2)SoftReference(软引用)
在内存空间足够,垃圾回收器不会回收它;如果内存空间不足,垃圾回收器就会回收软引用的对象。
(3)WeakReference(弱引用)
弱引用相对软引用,具有更短暂的生命周期。常规的GC,只要被扫描到,都会直接被回收。
(4)PhantomReference(虚引用)
虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。没用过。。。
选其一FIFOLimitedMemoryCache进行分析,构造时,必须要指定大小(单位:字节)。当设置的大小超过16MB(Android默认分配的大小好像也是这个)时,会有警告。
public LimitedMemoryCache(int sizeLimit) {
this.sizeLimit = sizeLimit;
cacheSize = new AtomicInteger();
if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB);
}
}
而FIFOLimitedMemoryCache的实现也相对简单,只要你清楚FIFO的基本概念:先进先出。
public class FIFOLimitedMemoryCache extends LimitedMemoryCache<String, Bitmap> { 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 void remove(String key) {
Bitmap value = super.get(key);
if (value != null) {
queue.remove(value);
}
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);
}
}
在removeNext()方法中,可以看到每次都是移除队列中的第一个对象。
Part3.1就此结束。
原来,用心写一篇文章,还是相当的困难。前前后后,走读,分析,类图,补充部分遗漏的知识点,足足用了半天的时间。不过,收获良多。
Part3.2主要是对core包中的下载设计技巧进行分析、研究,core.assist、core.display只会简单过一下。
Part3.3重在如何在实际项目中运用Android-Universal-Image-Loader,如何自行扩展部分功能。
估计,Part3.2/3.3会晚些日子才会更新,后面会对自己做的一些项目进行一定的总结。希望大家多多支持。
有任何问题,请邮件至:Osmondy@sina.com
浅谈开源项目Android-Universal-Image-Loader(Part 3.1)的更多相关文章
-
[原创] 浅谈开源项目Android-Universal-Image-Loader(Part 3.1)
最近,总算有时间去做些平时喜欢而没空去做的事情.一直觉得项目中使用的Image Loader适用性不强,昨晚在github随便逛逛,发现一个开源项目Android-Universal-Image-Lo ...
-
android universal image loader 缓冲原理详解
1. 功能介绍 1.1 Android Universal Image Loader Android Universal Image Loader 是一个强大的.可高度定制的图片缓存,本文简称为UIL ...
-
浅谈 MVP in Android(转)
我自己写的demo:https://pan.baidu.com/s/1dFImVYD 一.概述 对于MVP(Model View Presenter),大多数人都能说出一二:“MVC的演化版本”,“让 ...
-
浅谈 Angular 项目实战
为什么使用 Angular 我不是 Angular 的布道者,但如今痴迷 Angular,使用 Angular 做项目让我有一种兴奋感.目前的三大主流前端框架都研究过,博客中也有三者的相关教程,最早接 ...
-
浅谈 MVP in Android
一.概述 对于MVP(Model View Presenter),大多数人都能说出一二:“MVC的演化版本”,“让Model和View完全解耦”等等.本篇博文仅是为了做下记录,提出一些自己的看法,和帮 ...
-
浅谈Androidclient项目框架
写Android也有些时间了,一边工作,一边学习,一边积累.仅仅有遇到问题了,花时间去研究,自己的能力才干提升.刀假设不用.慢慢的就会生锈应该也是这个道理吧!上个月公司项目server框架进行的一些调 ...
-
Android Universal Image Loader java.io.FileNotFoundException: http:/xxx/lxx/xxxx.jpg
前段时间在使用ImageLoader异步加载服务端返回的图片时总是出现 java.io.FileNotFoundException: http://xxxx/l046/10046137034b1c0d ...
-
浅谈tornado项目应用设计
一.预备知识 最近开始尝试做一些tornado商城项目,在开始之前需要引入一些项目设计知识,如接口,抽象方法抽象类,组合,程序设计原则等,个人理解项目的合理设计可增加其灵活性,降低数据之间的耦合性,提 ...
-
[开源项目] Android校验库 - FireEye
简单易用的Android校验库. 这是一个简单Android校验库,按配置来验证用户输入的表单信息. 仅仅须要几行代码,就可以验证用户输入,而且将验证错误反馈给用户. 它内置了大量经常使用的验证类型, ...
随机推荐
-
新广告法,极限词剔除,替换掉的mysql语句
*,世界级,*, 最佳,最大,第一, 唯一,首个,首选, 最好,最大,精确, *,最高,最低, 最,最具,最便宜, 最新,最先进,最大程度, 最新技术,最先进科学,*产品, 填补国内空白 ...
-
网络html查看器
1)演示效果:
-
小心对待query_cache_size
作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究. 对于使用My ...
-
Newlife商业源码分享
[商业源码]生日大放送-Newlife商业源码分享 今天是农历六月二十三,是@大石头的生日,记得每年生日都会有很劲爆的重量级源码送出,今天Newlife群和论坛又一次疯狂了,吃水不忘挖井人,好的东西肯 ...
-
TextView 设置图片
TextView 设置图片 2012-05-17 15:12:38| 分类: Android | 标签:android textview图片 |举报|字号 订阅 Drawable im ...
-
hdu--1711--kmp应用在整形数组--Number Sequence
/* Name: hdu--1711--Number Sequence Author: shen_渊 Date: 16/04/17 19:58 Description: 第一次知道,KMP能用在整形数 ...
-
Linux指令--df,du
linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息. 1.命令格式: df [选项] [文件] 2.命 ...
-
利用STM32CubeMX生成HID双向通讯工程
使用开发板为正点原子ministm32 现在我们先使用HID descriptor Tool来生成我们需要的hid的 保存使用选择.H // D:\usb资料\HID\MSDEV\Projects\t ...
-
2018.07.01洛谷P2617 Dynamic Rankings(带修主席树)
P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i ...
-
ORA-28547:(Navicat Premium连接oracle报错)
1.背景 Navicat Premium连接oracle报 ORA-28547:connection to server failed, probable Oracle Net admin errro ...