DiskLruCache是Google官方提供的一种磁盘缓存的方案。如果用户每次获取图片都要从网上去获取,这将会非常耗费流量,而且效果也不太好,而DiskLruCache则很好地解决这个问题,只要下载一次,就将这张图片写入磁盘缓存,接下来每次从磁盘缓存中获取就可以了,省时省流量,可惜DiskLruCache暂时还没有写进SDK中,所以只能自己上网去下载源代码放进自己的项目中去。看看它是如何使用的:
创建DiskLruCache
DiskLruCache是不能通过new 创建实例化的,只能使用它提供的open方法来创建自身
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
这里有四个参数,分别的含义是磁盘缓存的文件,第二个参数是app的版本号,当版本号发生改变的时候,缓存就会被删除,所以一般设为1就可以了,第三个参数表示单个节点所对应的数据个数,一般设为1,第四个参数是缓存的大小。
注意:
- 磁盘缓存文件这里,通常要判断一下文件夹是否存在,如果不存在,就要新建一个。通常都会自己写一个方法,来获取磁盘的缓存文件,当SD卡存在的情况,就存放在SD中
public File getDiskCacheDir(Context context,String fileName){
boolean externalStorageAvailable = Environment
.getExternalStorageDirectory().equals(Environment.MEDIA_MOUNTED);
//磁盘缓存的绝对路径
final String cachePath;
//当SD卡存在的情况下
if (externalStorageAvailable){
cachePath = context.getExternalCacheDir().getPath();
}else{
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + fileName);
}
- 判断文件夹的大小是否满足所需的缓存大小,调用下列方法,就可以获取文件的可用空间大小
private long getUsableSpace(File path) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
return path.getUsableSpace();
}
final StatFs stats = new StatFs(path.getPath());
return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
}
所以标准的一个创建缓存方法是:
File diskCacheFile = getDiskCacheDir(context,"diskLruCache");
//如果文件不存在,则需要手动创建
if (!diskCacheFile.exists()){
diskCacheFile.mkdirs();
}
//当磁盘空间大于所需空间的时候,才能进行磁盘缓存
if(getUsableSpace(diskCacheFile)>DISK_CACHE_SIZE){
try {
mDiskLruCache = DiskLruCache.open(diskCacheFile,1,1,DISK_CACHE_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
写入缓存
DiskLruCache缓存添加时通过DiskLruCache.Editor来完成。首先,假如这里有一张图片的url=”http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg”,我们首先要获取这个url的key值,这个key值我们一般是用url的md5,因为url里面可能存在一些特殊符号,而且md5值也是唯一的。
private String hashKeyFormUrl(String url){
String cacheKey = "";
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(url.hashCode());
}
return cacheKey;
}
private String bytesToHexString(byte[] bytes){
StringBuilder sb = new StringBuilder();
for (int i=0;i<bytes.length;i++){
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1){
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
当得到这个key值的时候,就可以得到editor,并且根据这个editor就可以获得一个输出流
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
OutputStream out = editor.newOutputStream(0);
有了输出流之后,就可以当从网络下载图片的时候,图片就可以根据这个输出流写进文件系统。
private boolean downloadUriToStream(String urlString,OutputStream outputStream){
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);
out = new BufferedOutputStream(outputStream,IO_BUFFER_SIZE);
int b;
while ((b=in.read()) != -1){
out.write(b);
}
return true;
} catch (IOException e) {
e.printStackTrace();
}finally {
if (urlConnection != null){
urlConnection.disconnect();
}
try {
if (in != null){
in.close();
}
if (out != null){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
最后再通过editor.commit()来提交,最终的代码就是这样的:
/**
* 写入缓存
* @param uri
*/
public void add2DiskLruCache(final String uri){
new Thread(new Runnable() {
@Override
public void run() {
//将uri的MD5作为key
String key = hashKeyFormUrl(uri);
try {
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null){
OutputStream out = editor.newOutputStream(0);
if (downloadUriToStream(uri,out)){
editor.commit();
}else{
editor.abort();
}
}
mDiskLruCache.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
因为网络操作是一个耗时操作,不能将其放在主线程中运行,否则就会容易产生ANR。所以网络请求的操作需要在一个新的线程中执行。
读取缓存
获取缓存的话,就需要使用DiskLruCache.SnapShot这个对象。通过DiskLruCache.get(key)获取。当拿到这个对象之后,我们该怎样做呢?其实根据snapShot.getInputStream()就可以获取一个输入流,有了输入流之后,就简单多了,BitmapFactory.decodeStream(inputStream)就可以获取bitmap。
public Bitmap getDiskLruCache(String uri){
String key = hashKeyFormUrl(uri);
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null){
InputStream inputStream = snapshot.getInputStream(0);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
这是根据《Android 开发艺术探索》所写的。