Android 图片三级缓存
图片缓存的原理
实现图片缓存也不难,需要有相应的cache策略。这里采用 内存-文件-网络
三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结
构中。
当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图
片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个
url被下载过,其图片就被缓存起来了。
关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空
间足够,垃
圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高
速缓存。使用软引用能防止内存泄露,增强程序的健壮性。
--以上内容选自网络--
1、内存缓存
/** * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。 * 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。 */ private static LruCache<String, Bitmap> mLruCache; // 硬引用缓存 private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; // 软引用缓存 private static final int SOFT_CACHE_SIZE = 15; //软引用缓存容量
主要的构造初始化方法
public ImageLoader(Context context) {
int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
int cacheSize = 1024 * 1024 * memClass / 4; // 硬引用缓存容量,为系统可用内存的1/4
mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
if (value != null)
return value.getRowBytes() * value.getHeight();
else
return 0;
}
@Override
protected void entryRemoved(boolean evicted, String key,
Bitmap oldValue, Bitmap newValue) {
if (oldValue != null)
// 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
}
};
mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
private static final long serialVersionUID = 6040103833179403725L;
@Override
protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
if (size() > SOFT_CACHE_SIZE){
return true;
}
return false;
}
};
}
***最主要的获取图片的逻辑方法***
/**
* 加载获取图片的主要方法
*/
public Bitmap getBitmap(String url) {
// 从内存缓存中获取图片
Bitmap result = getBitmapFromCache(url);
if (result == null) {
// 文件缓存中获取
result = getBitmapFromFile(url);
if (result == null) {
// 从网络获取
result = getBitmapFromNet(url);
if (result != null) {
saveBitmap(result, url);
addBitmapToCache(url, result);
}
} else {
// 添加到内存缓存
addBitmapToCache(url, result);
}
}
return result;
}
从内存缓存中获取图片
/**
* 从内存缓存中获取图片
*/
public Bitmap getBitmapFromCache(String url) {
Bitmap bitmap;
//先从硬引用缓存中获取
synchronized (mLruCache) {
bitmap = mLruCache.get(url);
if (bitmap != null) {
//如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
mLruCache.remove(url);
mLruCache.put(url, bitmap);
return bitmap;
}
}
//如果硬引用缓存中找不到,到软引用缓存中找
synchronized (mSoftCache) {
SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null) {
//将图片移回硬缓存
mLruCache.put(url, bitmap);
mSoftCache.remove(url);
return bitmap;
} else {
mSoftCache.remove(url);
}
}
}
return null;
}
2、文件缓存
图片文件的保存
/** 将图片存入文件缓存 **/
public void saveBitmap(Bitmap bm, String url) {
if (bm == null) {
return;
}
// 判断sdcard上的空间
if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
// SD空间不足
return;
}
String filename = convertUrlToFileName(url);
String dir = getDirectory();
File dirFile = new File(dir);
if (!dirFile.exists())
dirFile.mkdirs();
File file = new File(dir + "/" + filename);
try {
file.createNewFile();
OutputStream outStream = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
/** 修改文件的最后修改时间 **/
public void updateFileTime(String path) {
File file = new File(path);
long newModifiedTime = System.currentTimeMillis();
file.setLastModified(newModifiedTime);
}
/** 计算sdcard上的剩余空间 **/
private int freeSpaceOnSd() {
StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
.getPath());
@SuppressWarnings("deprecation")
double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
.getBlockSize()) / MB;
return (int) sdFreeMB;
}
/** 将url转成文件名 **/
private String convertUrlToFileName(String url) {
String[] strs = url.split("/");
return strs[strs.length - 1] + WHOLESALE_CONV;
}
/** 获得缓存目录 **/
private String getDirectory() {
String dir = getSDPath() + "/" + CACHEDIR;
return dir;
}
/** 取SD卡路径 **/
private String getSDPath() {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory(); // 获取根目录
}
if (sdDir != null) {
return sdDir.toString();
} else {
return "";
}
}
图片文件的获取
/**
* 从文件缓存中获取图片
**/
public Bitmap getBitmapFromFile(final String url) {
final String path = getDirectory() + "/" + convertUrlToFileName(url);
File file = new File(path);
if (file.exists()) {
Bitmap bmp = BitmapFactory.decodeFile(path);
if (bmp == null) {
file.delete();
} else {
updateFileTime(path);
return bmp;
}
}
return null;
}
3、网络获取图片
/**
* 从网络获取图片
*/
public Bitmap getBitmapFromNet(String url) {
final HttpClient client = new DefaultHttpClient();
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
FilterInputStream fit = new FlushedInputStream(inputStream);
return BitmapFactory.decodeStream(fit);
} finally {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
entity.consumeContent();
}
}
} catch (IOException e) {
getRequest.abort();
} catch (IllegalStateException e) {
getRequest.abort();
} catch (Exception e) {
getRequest.abort();
} finally {
client.getConnectionManager().shutdown();
}
return null;
}
/*
* An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
*/
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
源码文件http://files.cnblogs.com/files/pear-lemon/ImageLoader.zip
Android 图片三级缓存的更多相关文章
- Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference))
因为之前项目同事使用了图片三级缓存,今天整理项目的时候发现同事还是使用了软引用(SoftRefrerence)和弱引用(WeakReference),来管理在内存中的缓存.看到这个我就感觉不对了.脑海 ...
- 【Android - 进阶】之图片三级缓存的原理及实现
在Android开发中,如果图片过多,而我们又没有对图片进行有效的缓存,就很容易导致OOM(Out Of Memory)错误.因此,图片的缓存是非常重要的,尤其是对图片非常多的应用.现在很多框架都做了 ...
- picasso_强大的Android图片下载缓存库
tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...
- 毕加索的艺术——Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选
毕加索的艺术--Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选 官网: http://square.github.i ...
- Android图片二级缓存
点击下载源代码 想起刚開始写代码的时候,领导叫我写一个头像下载的方法,当时屁颠屁颠就写了一个图片下载的,每次都要去网络上请求,最后直接被pass掉了 当时的思路是这种 后来渐渐地就知道了有二级缓存这东 ...
- picasso-强大的Android图片下载缓存库
编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! pica ...
- android图片的缓存--节约内存提高程序效率
如今android应用占内存一个比一个大,android程序的质量亟待提高. 这里简单说说网络图片的缓存,我这边就简单的说说思路 1:网络图片,无疑须要去下载图片,我们不须要每次都去下载. 维护一张表 ...
- 如何使用picasso 对Android图片下载缓存
相比较其他,picasso的图片缓存更加简单一些,他只需要一行代码就可以表述:导入相关jar包 Picasso.with(context).load("图片路径").into(Im ...
- Android图片载入缓存框架Glide
Glide开源框架是Google推荐的图片载入和缓框架,其在Github上的开源地址是:https://github.com/bumptech/glide 当然一个Google推荐的框架肯定就是Vol ...
随机推荐
- [Linux][Hadoop] 将hadoop跑起来
前面安装过程待补充,安装完成hadoop安装之后,开始执行相关命令,让hadoop跑起来 使用命令启动所有服务: hadoop@ubuntu:/usr/local/gz/hadoop-$ ./sb ...
- Objective-C编码规范:26个方面解决iOS开发问题(转)
链接
- hdu 3183(贪心)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183 思路:比较前后两个相邻的字符,如果前面一个字符大于后面一个字符,就把它去掉. #include ...
- CSS实现打字效果
.print{ width:250px; white-space:nowrap; overflow:hidden; -webkit-animation: dy 3s steps(60, end) in ...
- DOM兼容
-firstChild firstElementChild var oFirst = oUl.firstChild || oUl.firstElementChild; -lastChild la ...
- SQL作业的操作全
--定义创建作业 转自http://hi.baidu.com/procedure/blog/item/7f959fb10d76f95d092302dd.html DECLARE @jobid uniq ...
- 【maven】maven各种奇葩问题
问题1:Could not calculate build plan: Plugin org.apache... 不能成功创建maven项目 解决方法1: http://repo1.maven.org ...
- Ajax实现点击省份显示相应城市
功能:不用级联效果,自己写ajax,从接口读取省份城市数据,实现点击省份显示相应城市.后端根据省份ID,给前端返回城市. 一.DOM结构(套用blade模板) <div class=" ...
- 简单的Flume和hive的结合
1. 日志格式 #Software: Microsoft Internet Information Services 6.0 #Version: 1.0 #Date: -- :: #Fields: d ...
- HIT2715 Matrix3(最小费用最大流)
题目大概说有一个n×n的矩阵,每个格子都有权值和高度,在这个矩阵中进行最多k次旅行,每次旅行能从当前格子走到相邻且高度更小的格子,走到格子边界就能出去完成这次旅行.每走到一个格子就累加格子的权值然后把 ...