本文主要介绍一个支持图片自动预取、支持多种缓存算法、支持数据保存和恢复的图片Sd卡缓存的使用、功能及网友反馈的常见问题解答。
需要二级缓存或ListView和GridView图片加载请优先使用ImageCache。
更多 Android 图片缓存开源项目可见 Android 图片缓存。
与Android LruCache相比主要特性:(1). 使用简单 (2). 轻松获取及预取新图片 (3). 可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法 (4). 可方便的保存及初始化恢复数据 (5). 支持文件sd卡保存及自定义文件名规则 (6). 省流量性能佳(有且仅有一个线程获取图片) (7). 支持不同类型网络处理 (8). 可根据系统配置初始化缓存 (9). 扩展性强 (10). 支持等待队列 (11). 包含map的大多数接口。
适用:获取图片较多且图片较大的应用,如新浪微博大图、twitter大图、微信图片、美丽说、蘑菇街、花瓣、淘宝等等。
本文以用ImageSDCardCache实现Gallery为例,示例代码地址见ImageSDCardCacheDemo,效果图如下:
Demo APK 可以方便的查看效果,在各大应用商店搜索 trinea android 下载即可,如:Google Play。
1、使用
(1)引入公共库
引入TrineaAndroidCommon@Github(欢迎star和fork^_^)作为你项目的library,或是自己抽取其中的ImageSDCardCache.java部分使用。
(2)调用
仅需简单三步:
a. AndroidManifest.xml中添加权限
1 2 |
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
b.定义缓存和回调接口
基本使用:只对ImageView加载图片有效,定义只需要一条语句
1 |
public static final ImageSDCardCache IMAGE_SD_CACHE = CacheManager.getImageSDCardCache(); |
高级使用:自定义图片获取成功的回调接口,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/** init cache **/ public static final ImageSDCardCache IMAGE_SD_CACHE = new ImageSDCardCache(); static { OnImageSDCallbackListener imageCallBack = new OnImageSDCallbackListener() { private static final long serialVersionUID = 1L; // callback function before get image, run on ui thread @Override public void onPreGet(String imageUrl, View view) { // Log.e(TAG_CACHE, "pre get image"); } // callback function after get image successfully, run on ui thread @Override public void onGetSuccess(String imageUrl, String imagePath, View view, boolean isInCache) { ImageView imageView = (ImageView)view; Bitmap bm = BitmapFactory.decodeFile(imagePath); if (bm != null) { imageView.setImageBitmap(bm); } } // callback function after get image failed, run on ui thread @Override public void onGetFailed(String imageUrl, String imagePath, View view, FailedReason failedReason) { Log.e(TAG_CACHE, new StringBuilder(128).append("get image ").append(imageUrl).append(" error")); } }; IMAGE_SD_CACHE.setOnImageSDCallbackListener(imageCallBack); IMAGE_SD_CACHE.setCacheFolder(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "TrineaAndroidCommon"); } |
c. 需要加载图片的地方调用get(String imageUrl, View view)异步加载图片
1 |
IMAGE_SD_CACHE.get(imageUrl, imageView); |
上面代码中new ImageSDCardCache()用来定义图片缓存,默认会根据系统内存设置缓存大小。默认缓存算法为LFU(Least Frequently Used),最近最少使用先删除。
setOnImageSDCallbackListener(imageCallBack);设置图片获取成功回调接口,其中onPreGet为图片获取前的回调函数,onGetSuccess为图片获取成功的回调函数,onGetFailed为图片获取失败的回调函数。
setCacheFolder(String cacheFolder) 设置图片文件保存路径,默认为ImageSDCardCache#DEFAULT_CACHE_FOLDER
setFileNameRule(FileNameRule fileNameRule) 设置图片保存时文件名规则,默认根据url命名,见2.4部分详细介绍
可以在程序退出时调用saveDataToDb(Context context, String tag)保存数据,在程序启动时调用initData(Context context, String tag)初始化恢复数据。其中tag为此ImageCache的标识。
get(imageUrl, imageView)异步获取图片,在图片获取成功后会自动调用onImageLoaded将参数传入。
get(String imageUrl,List<String> urlList, View view)异步获取图片,并且会自动预取urlList中的图片。
上面是最简单的实现。
第2部分会介绍支持的其他功能,包括网络类型设置、预取个数设置、多种缓存算法、图片SD卡保存目录及文件名规则设置、保存及初始化恢复数据、等待队列开关、图片源读取方式设置、读取图片http设置、缓存有效时间、命中率及大部分map接口。
2、功能介绍
(1) 多种构造函数,可根据系统配置初始化缓存
public ImageSDCardCache()
public ImageSDCardCache(int maxSize)
public ImageSDCardCache(int maxSize, int threadPoolSize)
支持三种构造函数,支持缓存大小和获取图片线程池大小的设置。默认会根据系统可用内存大小设置缓存大小,根据系统Cpu个数设置线程池大小。
(2)、获取图片及自动预取
get(String imageUrl, View view)异步获取图片,在图片获取成功后自动调用OnImageSDCallbackListener的onImageLoaded函数,返回是否已在缓存中
get(String imageUrl, List<String> urlList, View view)异步获取图片,在图片获取成功后自动调用OnImageSDCallbackListener的onImageLoaded函数,并且根据imageUrl在urlList中的位置向前向后预取图片,返回是否已在缓存中。
public void setForwardCacheNumber(int forwardCacheNumber) 向前预取图片个数设置,默认为PreloadDataCache#DEFAULT_FORWARD_CACHE_NUMBER
public void setBackwardCacheNumber(int backwardCacheNumber)向后预取图片个数设置默认,默认为PreloadDataCache#DEFAULT_BACKWARD_CACHE_NUMBER
public CacheObject<V> get(K key)
public CacheObject<V> get(K key, List<K> keyList)
两个接口是直接同步获取图片,且获取成功后不会调用OnImageSDCallbackListener的onImageLoaded函数
(3)、设置缓存算法
setCacheFullRemoveType(CacheFullRemoveType<String> cacheFullRemoveType)
设置缓存算法,缓存算法即为缓存满时为了插入新数据,删除旧数据的规则。
目前包括FIFO、LIFO、LRU、MRU、LFU、MFU、优先级低先删除、优先级高先删除、数据小先删除、数据大先删除、图片小先删除、图片大先删除、永不删除。还可以通过实现CacheFullRemoveType来自定义缓存算法。。默认为RemoveTypeUsedCountSmall,即LRU使用频率低先删除。下面详细介绍各个算法:
RemoveTypeEnterTimeFirst FIFO先进先出,先进入先删除
RemoveTypeEnterTimeLast LIFO后进先出,后进入先删除
RemoveTypeLastUsedTimeFirst LRU(Least Recently User),最先使用先删除
RemoveTypeLastUsedTimeLast MRU(Most Recently Used),最近使用先删除
RemoveTypeUsedCountSmall LFU(Least Frequently Used),使用频率低先删除
RemoveTypeUsedCountBig MRU(Most Frequently Used),使用频率高先删除
RemoveTypePriorityLow 优先级低先删除
RemoveTypePriorityHigh 优先级低先删除
RemoveTypeFileSmall 文件小的先删除
RemoveTypeFileLarge 文件大的先删除
RemoveTypeDataBig 数据大先删除,根据缓存数据的compareTo函数决定
RemoveTypeDataSmall 数据小先删除,根据缓存数据的compareTo函数决定
RemoveTypeNotRemove 不删除,缓存满时不再允许插入新数据
自定义缓存算法只需要实现CacheFullRemoveType的compare方法即可。比较结果小于0表示会被先删除
1 2 3 4 5 6 7 8 9 10 |
public class MyRemoveType<T> implements CacheFullRemoveType<T> { private static final long serialVersionUID = 1L; @Override public int compare(CacheObject<T> obj1, CacheObject<T> obj2) { // process and return, smaller than zero means may be removed return ***; } } |
(4)、设置图片SD卡保存目录及文件名规则
setCacheFolder(String cacheFolder) 设置图片文件保存路径,默认为ImageSDCardCache#DEFAULT_CACHE_FOLDER
setFileNameRule(FileNameRule fileNameRule) 设置图片保存时文件名规则,默认根据url命名。已有命名规则有:
FileNameRuleImageUrl 根据图片url命名,url中所有非数字字母都会替换为_
FileNameRuleCurrentTime 根据当前图片获取成功时间命名,支持各种时间格式,见FileNameRuleCurrentTime#TimeRule
自定义文件命名规则算法只需要实现FileNameRule的getFileName方法即可。返回结果即为文件名,注释不能包含文件名以外的非法字符
1 2 3 4 5 6 7 8 9 10 |
public class MyFileNameRule implements FileNameRule { private static final long serialVersionUID = 1L; @Override public String getFileName(String imageUrl) { // process and return, the result need not contain char which not allowed in File name return ****; } } |
(5)、保存及初始化恢复数据
public boolean saveDataToDb(Context context, String tag)
保存数据到数据库,可在程序退出时调用,不建议在activity onDestrory时调用,而是整个程序退出时,见本文3.1常见问题解答。
public void initData(Context context, String tag)
初始化恢复数据,可在程序刚开始加载时调用,不建议在每个activity oncreate调用,而是整个程序初始化时,见本文3.1常见问题解答。
(6)、是否启用队列
setOpenWaitingQueue(boolean isOpenWaitingQueue)
当不同view通过get函数获取图片时,是否开启等待队列。
若开启,保存所有view,图片获取成功后依次调用OnImageSDCallbackListener的onImageLoaded函数;否则仅保存最后调用get的view,图片获取成功后调用OnImageSDCallbackListener的onImageLoaded函数
默认开启队列等待。如果希望最优性能且场景满足,可设置为false。
(7)、设置图片获取方式接口
setOnGetDataListener(OnGetDataListener<String, String> onGetDataListener)
设置图片获取的方式。缓存通过该接口获取图片及预取图片。默认为ImageSDCardCache中getDefaultOnGetImageListener,通过url获取图片。你可以重写该方法将图片缓存改为本地图片文件缓存等等。
(8)、读取图片http设置
a. 设置图片读取http超时
public void setHttpReadTimeOut(int readTimeOutMillis)
若readTimeOutMillis小于0表示不设置超时,默认不设置,单位为毫秒
b. 设置无网络不读取
public void setContext(Context context)
设置context,网络连接失败不会新建线程请求图片。
c. 支持不同网络类型的处理
public void setAllowedNetworkTypes(int allowedNetworkTypes)
设置允许的网络类型,可选择PreloadDataCache#NETWORK_MOBILE、PreloadDataCache#NETWORK_WIFI或两者都允许。默认两者都允许。
注意:这个接口生效必须先setContext(Context context)
d. 设置读取图片http请求属性
public void setRequestProperties(Map<String, String> requestProperties)
public void setRequestProperty(String field, String newValue)
设置获取图片时的http request属性,如
setRequestProperty(“User-Agent”, “TrineaAndroidCommon/4.0 (Android 4.0) MI_2S”);设置User-Agent头信息
setRequestProperty(“Connection”, “false”);设置connection keep alive为false,默认为true
(9)、缓存元素有效时间
setValidTime(long validTime)
设置缓存元素有效时间,小于0表示不会失效,此时仅根据CacheFullRemoveType在缓存满时替换元素
通过protected boolean isExpired(K key)判断某key是否过期
(10)、缓存命中率
getHitRate()、getHitCount()、getMissCount()分别表示缓存命中率、命中次数、未命中次数
(11)、其他与map类似接口
public boolean containsKey(K key) 缓存中是否包含该key
public CacheObject<V> remove(K key) 从缓存中删除某个key
public void clear() 清空缓存
public Set<K> keySet() 缓存中key集合
public Set<Map.Entry<K, CacheObject<V>>> entrySet() 缓存中key,value键值对集合
public Collection<CacheObject<V>> values() 缓存中元素集合
public CacheObject<V> put(K key, V value) 手动插入某个元素
public CacheObject<V> put(K key, CacheObject<V> value) 手动插入某个元素
public CacheObject<V> get(K key) 得到某个key
public int getSize() 得到缓存中有效元素个数
public int getMaxSize() 得到缓存中元素最大个数
(12)得到设置
上面的set几乎都可以通过对应的get得到相应value
使用getOnGetDataListener()得到获取图片的方法
getForwardCacheNumber()得到自动向前缓存的个数
getBackCacheNumber()得到自动向后缓存的个数
getMaxSize()得到缓存最大容量
getValidTime()得到有效时间,以毫秒计
getCacheFullRemoveType()得到cache满时删除元素类型
3、常见问题解答
1、是每个Activity一个ImageSDCardCache还是整个程序共用一个?
建议ImageSDCardCache缓存整个app用同一个实例,而不是每个activity一个。在程序启动时initData初始化图片缓存,退出saveDataToDb保存图片缓存。因为:
a. 你的程序总不能就一个activity需要图片缓存,缓存本身就该应该所有页面共用
b. 每次进activity新建一个缓存只会增加系统消耗,没有必要
c. 这个缓存不会占用太多内存,所以程序运行期间一直存在也不会对性能有多大影响
2、为什么应用退出后会重新获取图片?为什么应用退出后无网络情况下之前缓存的图片不会自己加载?
在程序退出(比如onDestroy函数)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如onCreate函数)时调用initData(Context context, String tag)初始化数据。其中tag为此ImageCache的标识。
3、为什么无法加载图片?
可通过OnImageSDCallbackListener中的onGetFailed函数FailedReason参数判断失败原因。目前错误原因包括sdcard保存出错(请确保存在外部sdcard)和网络出错。
4、能否直接加载sdcard中已经存在的图片,即数据源为本地sdcard而不是网络?
可以通过setOnGetDataListener(OnGetDataListener<String, String> onGetDataListener)设置图片获取方式
5、配合ListView和GridView使用时卡顿?
请使用ImageCache,包含二级缓存,性能更佳
6、其他
ListView滑动过程中图片显示重复错乱闪烁问题解决
图片OutOfMemory异常bitmap size exceeds VM budget的原因及解决方法
楼主你好 图片SD卡缓存 在网络上获取不到这个图片的时候 如何捕捉到其错误信息 然后进行相应的错误处理
这个接口还没开放,一周内会开放,你也可以根据源码自己修改下
我研究了大半天源码 还是没搞出来 谢谢了。
继承的层次有点多了,源码现在有点复杂了,这个需求你主要用来做什么
读取网络图片 我做了一个progressbar 如果网络图片地址有误的话,捕捉到这个异常,或者返回一个null值一个 我就能做处理了 显示图片不存在 隐藏掉 progressbar
暂时可以直接看上面调用示例中OnImageSDCallbackListener的onImageLoaded函数中的写法,判断bitmap是否为空,空则表示异常
我调试过,但是 如果网络图片地址有误的话 是不会跳转到 OnImageSDCallbackListener的onImageLoaded函数里面的 ,会打印出一个异常。
这个地方有bug,我待会儿会提交了
好的 谢谢了
api和demo两个工程都已经更新,晚上会把博客也更新了
楼主你好,我在用SDcard缓存时出现了OOM,发现是用这个Bitmap bm = BitmapFactory.decodeFile(imagePath, option);方法直接读取sd卡图片,会比较容易造成OOM,请教楼主这个地方能不能再优化下用更好的方法呢
你图片很大吗?或者ImageSDCardCache(int maxSize)初始化时将缓存最大值减小
楼主,内存缓存,和SD卡缓存如果不加 “可以在程序退出(比如onDestroy函数)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如onCreate函数)时调用initData(Context context, String tag)初始化恢复数据。其中tag为此ImageCache的标识。”这个,会是什么结果,我记得以前的版本没有这个啊
这对函数的主要功能是保证程序重新打开时不用重新获取之前已经获取过的图片。以前的版本退出后重新打开需要重新网络获取的
我想要手动插入图片到缓存中,是不是使用put方法,但是里面那个参数的类型应该怎么样生成ImageCacheInterface.IMAGE_SD_CACHE.put(key,这个value是什么类型的);本地的图片怎么样转换为这个类型
为什么要手动插入?
因为发送照片时本地存储了图片,然后显示的时候图片地址是http地址,这样就需要从网络上重新获取一次,如果这个时候将本地的图片和图片的http地址做手动关联的话,加载图片时,就不需要在重新获取了,呵呵,理论上,应该是这样的吧
是这样。你真正想要的是什么,读取本地图片而不是网络图片,还是说只是想网络读取后以后都读本地
我理解应该是,通过手动将本地拍摄的照片放入imagecache的缓存文件结构中,并将该图片标志为一个http远程的图片,从而实现基于缓存机制,不重复下载本地上传的图片。 一个典型的例子是:用户发送了照片A,送到服务器上的URL地址是HTTPURLB。由于照片A本身也要在用户的消息流中显示,所以将A放入imagecache,关联为HTTPURLB。基于imagecache的机制,此时照片A就无需重新下载,实现了快速展现。
哦,可以的,直接调用put(String url, String path)就可以了,url是图片之后的url,path就是路径,path不用符合getFileNameRule()的规则,这个规则只在缓存中url不存在网络获取到图片保存到本地时做为文件名生成规则。
就是说,我从服务器获取到了http的url,用这个url作为key,将一个sd卡的图片作为value,进行绑定,放入到内存
哦,可以的,直接调用put(String url, String path)就可以了,url是图片之后的url,path就是路径,path不用符合getFileNameRule()的规则,这个规则只在缓存中url不存在网络获取到图片保存到本地时做为文件名生成规则。
哦,好的,那我再试试看吧,谢谢咯
你好,还是不好用,代码如下:加入缓存ImageCacheInterface.IMAGE_SD_CACHE.put(pic,newname+”.jpg”);加载图片ImageCacheInterface.IMAGE_SD_CACHE.get(pic, contentIv);在不同的activity里面进行的put和get操作,发现get的时候依然会从别的服务器取一次
put和get在相同的activity里面也试了,也不行,同样会在服务器上下载一次
知道啥问题了,呵呵
如果要求性能更好的话,可以换成ImageCache
我发现一个问题,之前我使用put不管用,是因为我的put是写在try语句里面的,如下所示:try { String newname = pic.replace(“:”, “_”); saveMyBitmap(newname,imageFile.toString()); ImageCacheInterface.IMAGE_SD_CACHE.put(pic,newname+”.jpg”);} catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace();}当我改为如下写法,即把put写在try之外后就可以了,如下所示:String cachename = “”;String keystr = “”;try { String newname = pic.replace(“:”, “_”); cachename = newname; keystr = pic; saveMyBitmap(newname,imageFile.toString());} catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace();}if(!cachename.equals(“”)){ ImageCacheInterface.IMAGE_SD_CACHE.put(cachename,keystr+”.jpg”);}这是怎么回事呢
明显是saveMyBitmap(newname,imageFile.toString());这句话会抛出异常导致ImageCacheInterface.IMAGE_SD_CACHE.put(pic,newname+”.jpg”);不会被执行啊,你看看具体异常是啥
private void startGetImageThread(final int messsageWhat, final String imageUrl, final List urlList) { // wait for image be got success and send message threadPool.execute(new Runnable() { @Override public void run() { CacheObject object = get(imageUrl, urlList); String imagePath = (object == null ? null : object.getData()); // if image file not exist, remove it from cache and reload it if (StringUtils.isEmpty(imagePath) || !FileUtils.isFileExist(imagePath)) { remove(imageUrl); if (messsageWhat == IMAGE_LOADED_WHAT) { // 你这里的这段代码是不是有问题? startGetImageThread(IMAGE_RELOADED_WHAT, imageUrl, urlList); } } else { handler.sendMessage(handler.obtainMessage(messsageWhat, new MessageObject(imageUrl, imagePath, urlList))); } } }); }
我摘抄了你的案例,在 oncreate 方法中如下调用:IMAGE_SD_CACHE.get(“http://p3.zhimg.com/21/68/2168de7d6515c5493f77b4da235e7c20_m.jpg”, mLaunchImage);发现无法获取到网络图片!
示例demo在你机器上能正常运行吗?
这个是一个保护措施,为了防止数据还在缓存中但sdcard中图片被删除的情况,在这时候会重新获取一次
你好啊,这个版本如果把图片缓存到本地以后,如果没有网络的情况下本地图片依然加载不上,在没有网络的情况下还得自己来加载本地缓存图片
能不能仔细看看我给你的回复。。。在程序退出(比如onDestroy函数)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如onCreate函数)时调用initData(Context context, String tag)初始化数据。其中tag为此ImageCache的标识
现在图片的url在缓存里是明文显示的,我想把缓存的文件名简单加密下,请问缓存文件命名的代码是在哪里啊,找了半天没找到。。。
调用这个setFileNameRule(FileNameRule fileNameRule)接口,自己定义文件名规则
good!
现在我通过FileNameRule接口将文件名改成了url的hashcode,这样有种可能两个不同url的hashcode是相同的,就会导致之前的图片被新图片覆盖,进而导致文件索引错乱。现在是否有判断重名文件处理的接口处理这样的问题?
想到办法了,直接在接口里面判断就好了,一时短路。
这个的重复得遍历缓存中的values了,不太好。实际上你没必要用url的hashCode,可以考虑使用FileNameRuleCurrentTime.TimeRule.TO_MILLIS表示图片获取成功时的毫秒数,如果觉得不够精确,可以自己重写加上纳秒
好的,我先用毫秒数试一下。另外又遇到个问题,我一次性请求加载15张图片,然后在onImageLoaded中加了打印,发现一共打印了20次,为什么会有重复调用的问题呢?不知道是不是我调用方式有问题。
不知道你实际的场景是什么样的,什么View配合cache使用?不过onImageLoaded不管图片是否在缓存中都会走。getDefaultOnGetImageListener中的onGetData才是网络获取图片的地方。
现在测试直接使用15个imageview装载15张图片, public static void init(Context context) { mContext = context; imageCache = new ImageSDCardCache(); imageCache.setFileNameRule(new FileNameRuleCurrentTime(FileNameRuleCurrentTime.TimeRule.TO_MILLIS)); imageCache.setOnImageSDCallbackListener(new ImageSDCardCache.OnImageSDCallbackListener() { @Override public void onImageLoaded(String imageUrl, String imagePath, View view, boolean isInCache) { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); if (view != null && bitmap != null) { ((ImageView) view).setImageBitmap(bitmap); Log.e(imagePath + ” ” + imageUrl); } } }); } public static void getImage(String imageUrl, ImageView imageView) { imageCache.get(imageUrl, imageView); }现在我调用了getImage15次,但是onImageLoaded的打印却打印了20次,不知道是什么情况。。。
这个问题我晚上看下
10-19 11:02:01.538 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:01.548 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:01.698 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:01.718 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:01.938 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:01.938 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721599.jpeg http://imgst-dl.meilishuo.net/pic/f/7e/9a/34edd1eb52d861bc10e5f3e2f227_1240_1240.jpeg10-19 11:02:01.948 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721625.jpeg http://imgst-dl.meilishuo.net/pic/f/54/7f/00680c66ad6741c9599aeac1cdbf_680_456.jpeg10-19 11:02:01.958 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721639.jpg http://shanghai.wsxq.com/income/shopimg/200974132442.jpg10-19 11:02:01.958 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721678.jpg http://t2.baidu.com/it/u=1324255000,2127920415&fm=23&gp=0.jpg10-19 11:02:01.968 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721697.jpg http://t2.baidu.com/it/u=514885245,949751443&fm=23&gp=0.jpg10-19 11:02:01.968 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721711.jpg http://t1.baidu.com/it/u=3129115091,868118389&fm=23&gp=0.jpg10-19 11:02:01.988 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721690.jpg http://img.92dp.com/ProImgFile/20081121/92dp_20081121114439.jpg_150x150.jpg10-19 11:02:01.988 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721723.jpg http://img25.nipic.com/20110616/3234272_171247526000_1.jpg10-19 11:02:01.998 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721694.jpg http://img.aoofu.com/psell/1/7608/553163596.jpg10-19 11:02:01.998 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721746.jpg http://img35.dangdang.com/17/7/60275375-1_l.jpg10-19 11:02:02.008 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721732.jpg http://www.ddc.net.cn/upload/1023/1022458/20111009093502_big.jpg10-19 11:02:02.008 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721783.jpg http://img43.nipic.com/20130504/9746913_214749007314_1.jpg10-19 11:02:02.008 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151721705.jpg http://www.bikeyx.cn/images/goods/20120901/462e33f358d1c48e.jpg10-19 11:02:02.028 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:02.038 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:03.390 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:03.400 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:04.701 22654-22654/com.test.example E/ImageUtil﹕ @line:43 false /data/data/com.test.example/cache/1382151724702.jpg http://t3.baidu.com/it/u=3585313719,3770509884&fm=23&gp=0.jpg10-19 11:02:04.721 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg10-19 11:02:04.731 22654-22654/com.test.example E/ImageUtil﹕ @line:43 true /data/data/com.test.example/cache/1382151721272.jpg http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg这个是打印内容,发现有几个重复的部分,看下有没有帮助。
onImageLoaded只要调用了get(String, View)方法就会执行,不管图片是否在缓存中。通过打印你也可以看到http://t1.baidu.com/it/u=549646413,1542654075&fm=23&gp=0.jpg 这个图片你调用了多次,true表示在缓存中,false表示不在缓存中通过网络获取,false只有一次,所以只有一次网络获取,至于false在最后打印那是因为图片获取成功后调用之前的callback线程池中线程运行顺序不定但对结果没有影响。真正网络获取图片在getDefaultOnGetImageListener中的onGetData方法中。
查了下果然是调了多次。我是在gridadapter的getview里面调用的getImage的,为什么第一个imageview会被调用三次getview呢,不解。。。这个问题跟楼主的库没关系,纯咨询
这个跟gridview和listView的缓存有关系,对你程序没有影响的。部分原理可见:http://www.trinea.cn/android/android-listview%E6%BB%91%E5%8A%A8%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%9B%BE%E7%89%87%E6%98%BE%E7%A4%BA%E9%87%8D%E5%A4%8D%E9%94%99%E4%B9%B1%E9%97%AA%E7%83%81%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/
原因不是异步加载,不过同样是通过setTag的方法解决了,参考了这个文章http://aid2012.iteye.com/blog/1697012ps:你的博客后缀最好不要用中文吧,转码以后太长了…
嗯,早期的文章有这个问题,现在的都用英文了。确实不是异步加载,是这两个view缓存的复用机制导致的,给你那个链接只是让你了解下缓存机制而已。
求新版本。。。
正在弄
已更新,重新拉取代码。在程序退出(比如onDestroy函数)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如onCreate函数)时调用initData(Context context, String tag)初始化数据。其中tag为此ImageCache的标识。可参考:ImageSDCardCacheDemo
sd卡缓存中有使用内存缓存么?还是缓存成功后每次都要从sd卡中读取?
每次从sd卡取,所以不适合listview这样的控件,会有点慢,这块我正在改,这两天会有个二级缓存的版本发布
期待sd卡缓存和内存缓存的合体版。。。!
想请教一下,用了上面的方面,程序运行一次,缓存成功,在运行一次,有重新下载图片,不读缓存,这是什么原因呢,谢谢
是的,因为还没加本地数据库,所有程序推出前需要自己保存下cache中的enterset,其中key value分别为url和图片sd卡路径
是的,因为还没加本地数据库,所有程序推出前需要自己保存下cache中的enterset,其中key value分别为url和图片sd卡路径
可以贴一下保存cache中的enterset的代码不?程序再次运行的时候是不是还需要自己手动读取下?
是的,需要手动读取的,enterset就是map的接口
或者是否可以这样处理:图片的url是唯一的,现在缓存文件命名也是通过url命名的,当我需要获取一张网络图片时,不读取map表,而是直接去缓存目录找是否有该文件名的缓存文件,如果有就缓存命中。这样的话可以省掉map表管理及map的存储和读取,是否可行?
可行,但是严谨上讲这样程序对外部依赖太大,文件夹随便哪个其他程序都可以控制。后面可能会把数据库加上,初始读数据库
好的!很喜欢楼主的代码风格啊,很整齐。提几个需求,楼主考虑下是否添加:1. 添加手机机身内存缓存支持。2. 前面提到的添加二重缓存(内存缓存+sd卡缓存,及内存缓存+机身内存缓存)。另外还有个问题:现在支持同时最多多少张图片的读取?
感谢你的反馈,现在线程池开的并发数是2n+1, n为cpu个数,2n+1如果超过8就取8。你说的两个需求我都会在下周加上。
代码已更新,重新拉取代码。在程序退出(比如onDestroy函数)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如onCreate函数)时调用initData(Context context, String tag)初始化数据。其中tag为此ImageCache的标识。可参考:ImageSDCardCacheDemo
您好,通过这个能不能直接加载SD卡里的图片?
可见第二部分功能介绍中6的设置图片获取接口,从本地读取图片