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 ...
随机推荐
- hdu 4731 2013成都赛区网络赛 找规律
题意:找字串中最长回文串的最小值的串 m=2的时候暴力打表找规律,打表可以用二进制枚举
- [Tools] 使用XP远程登录Win8系统
[背景] 完成最基本的设置后,发现xp依然不能远程访问win8桌面,搜索后发现需要进一步设置 [开工] 按照参考资料进行设置,下面的参考资料已经写的很详细了,只是参考资料2中的文件名: redss ...
- [Liferay6.2]Liferay Dynamic Query API示例
介绍 Liferay提供了几种方法定义复杂的查询用来检索数据库中的数据. 通常情况下,在每个service Entity中,通过定义一些'finder'方法,可以便捷地满足基本的数据查询操作. 但是, ...
- ios内购
1.添加框架,storeKit.framework 需要真机调试 /* 内购五步: 1.请求可销售商品的列表 2.展示课销售的商品 3.点击购买 4.开具小票 5.创建交易对象并添加到交易队列 6.创 ...
- loadrunner取出关联数组中的所有元素
方法一: int num; char nameVar[100]; char nameValue[100]; lr_save_string("AAA","name_1&qu ...
- vs 中怎么用c改变部分字体颜色
// test.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <windows.h> #include< ...
- PHP 传值和传引用、传地址的区别
传值, 是把实参的值赋值给行参 那么对行参的修改,不会影响实参的值 传地址 是传值的一种特殊方式,只是他传递的是地址,不是普通的如int 那么传地址以后,实参和行参都指向同一个对象 ...
- js常用函数陆续总结
1.each() 方法规定为每个匹配元素规定运行的函数. $.each(data,function(index,item){ sb.append(item.answerNum); } $(" ...
- Thymeleaf模板引擎使用
Thymeleaf模板引擎使用 什么是Thymeleaf Thymeleaf是一个Java库.它是一个XML / XHTML / HTML5模板引擎,能够在模板文件上应用一组转换,将程序产生的数据或者 ...
- 【面经】【转】C++类型转换
C风格的强制类型转换(Type Cast)很简单,不管什么类型的转换统统是:type b = (type) a. C++风格的类型转换提供了4种类型转换操作符来应对不同场景的应用. const_cas ...