DiskLruCache(https://github.com/JakeWharton/DiskLruCache)想必大家都很熟悉。(不熟悉的请看这里),它是jakewharton大神写的一个开源库,提供了硬盘缓存的方案。
但是该库的API比较简单,有时候并不能满足我们使用。比如说如果你想把缓存中的数据以Bitmap的形式返回,API并没有提供这样的方法,我们必须通过DiskLruCache#get方法返回来的Snapshot获得输入流,然后将流转化为Bitmap。另外,构建DiskLruCache时必须要传入VersionCode,CachePath等变量,而实际开发中,Versioncode通常是应用版本号,Cachepath也相对固定(要么是sd的缓存目录要么是本地的缓存目录),每次构建DiskLruCache都要写获取版本号等等重复的代码实在是太枯燥了。再比如说,写缓存时需要指定key,而这个key在项目中通常是url,而url中可能有特殊字符,这将导致写缓存失败,我们最好将url进行编码,然后使用编码后的key,而在API中并没有提供类似的方法。鉴于此,我写了一个基于DiskLruCache的工具类,进一步简化缓存操作。
这个工具类主中的方法全部是静态的,可直接调用。它的功能有:1.构建缓存对象;2.读缓存。将缓存读为String/流/Bitmap;
3.写缓存。可以将String/File/流/Bitmap写入缓存,并支持异步写入。
说了这么多,下面来看这个工具类是怎样写的吧。
package com.jakewharton.disklrucache;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.text.TextUtils;
/**
* @author Rowandjj
*
*DiskLruCache的辅助工具类(DiskLruCache:http://jakewharton.github.io/DiskLruCache)
* 提供创建/读取/写入缓存的方法,并支持异步缓存写入.
*/
public class DiskLruCacheHelper
{
private static final long DEFAULT_MAX_SIZE = 10*1024*1024;
private static ExecutorService service = null;
/**
*
* 创建DiskLruCache实例,默认版本号为当前应用版本号,缓存位置由getDiskCacheDir指定
* @param context 上下文
* @param cacheDirName 缓存文件夹名称
* @param maxSize 缓存最大值,单位是byte
* @return 创建成功返回DiskLruCache实例否则返回null
*/
public static DiskLruCache createCache(Context context,String cacheDirName,long maxSize)
{
DiskLruCache cache = null;
try
{
cache = DiskLruCache.open(getDiskCacheDir(cacheDirName, context), getAppVersion(context),1, maxSize);
} catch (IOException e)
{
e.printStackTrace();
}
return cache;
}
/**
* 返回一个具有默认大小的DiskLruCache实例,默认大小为10Mb
* @param context
* @param cacheDirName
* @return 创建成功返回DiskLruCache实例否则返回null
*/
public static DiskLruCache createCache(Context context,String cacheDirName)
{
return createCache(context, cacheDirName, DEFAULT_MAX_SIZE);
}
/**
* 将图片写入缓存
*/
public static boolean writeBitmapToCache(DiskLruCache cache,Bitmap bitmap,String url)
{
return writeBitmapToCache(cache, bitmap, url, CompressFormat.JPEG,100);
}
/**
* 异步地将图片写入缓存。将不会不会写入结果。
* @param cache
* @param bitmap
* @param url
*/
public static void asyncWriteBitmapToCache(final DiskLruCache cache,final Bitmap bitmap,final String url)
{
if(service == null)
service = Executors.newSingleThreadExecutor();
service.execute(new Runnable()
{
@Override
public void run()
{
writeBitmapToCache(cache, bitmap, url);
}
});
}
/**
* 将图片写入缓存
* @param cache 缓存对象
* @param bitmap 图片对象
* @param url 用于标识bitmap的唯一名称,通常为图片url
* @return true表示写入缓存成功否则为false
*/
public static boolean writeBitmapToCache(DiskLruCache cache,Bitmap bitmap,String url,CompressFormat format, int quality)
{
if(cache == null || bitmap == null || url == null || TextUtils.isEmpty(url))
return false;
try
{
DiskLruCache.Editor editor = cache.edit(generateKey(url));
if(editor != null)
{
OutputStream out = editor.newOutputStream(0);
if(bitmap.compress(format,quality, out))
{
editor.commit();
return true;
}
else
{
editor.abort();
}
}
} catch (IOException e)
{
e.printStackTrace();
}
return false;
}
/**
* 异步地将图片写入缓存。将不会不会写入结果。
*/
public static void asyncWriteBitmapToCache(final DiskLruCache cache,final Bitmap bitmap,final String url,final CompressFormat format, final int quality)
{
if(service == null)
service = Executors.newSingleThreadExecutor();
service.execute(new Runnable()
{
@Override
public void run()
{
writeBitmapToCache(cache, bitmap, url,format,quality);
}
});
}
/**
* 异步地将inputStram流写入缓存,将不会返回写入结果。
*/
public static void asyncWriteStreamToCache(final DiskLruCache cache,final InputStream in,final String url)
{
if(service == null)
service = Executors.newSingleThreadExecutor();
service.execute(new Runnable()
{
@Override
public void run()
{
asyncWriteStreamToCache(cache, in, url);
}
});
}
/**
* 将inputStram流写入缓存
* @param cache
* @param in
* @param url
* @return
*/
public static boolean writeStreamToCache(DiskLruCache cache,InputStream in,String url)
{
if(cache == null || in == null || url == null|| TextUtils.isEmpty(url))
return false;
DiskLruCache.Editor editor = null;
try
{
editor = cache.edit(generateKey(url));
if(editor != null)
{
OutputStream out = editor.newOutputStream(0);
BufferedInputStream bin = new BufferedInputStream(in);
byte[] buffer = new byte[1024];
int len = 0;
while((len = bin.read(buffer)) != -1)
{
out.write(buffer, 0, len);
out.flush();
}
editor.commit();
return true;
}
} catch (IOException e)
{
e.printStackTrace();
}
return false;
}
/**
* 异步地将文件写入缓存,将不会返回写入结果。
*/
public static void asyncWriteFileToCache(final DiskLruCache cache,final File file,final String url)
{
if(service == null)
service = Executors.newSingleThreadExecutor();
service.execute(new Runnable()
{
@Override
public void run()
{
writeFileToCache(cache, file, url);
}
});
}
/**
* 将文件写入缓存
* @return true表示写入成功否则写入失败
*/
public static boolean writeFileToCache(DiskLruCache cache,File file,String url)
{
if(cache == null || file == null || url == null || !file.exists() || TextUtils.isEmpty(url))
{
return false;
}
FileInputStream fin = null;
try
{
fin = new FileInputStream(file);
} catch (FileNotFoundException e)
{
e.printStackTrace();
}
return writeStreamToCache(cache, fin, url);
}
/**
* 异步地将字符串写入缓存,将不会返回写入结果
*/
public static void asyncWriteStringToCache(final DiskLruCache cache,final String str,final String url)
{
if(service == null)
service = Executors.newSingleThreadExecutor();
service.execute(new Runnable()
{
@Override
public void run()
{
writeStringToCache(cache, str, url);
}
});
}
/**
* 将字符串写入缓存
* @param cache
* @param str
* @param url
* @return
*/
public static boolean writeStringToCache(DiskLruCache cache,String str,String url)
{
if(cache == null || str == null || url == null || TextUtils.isEmpty(url) || TextUtils.isEmpty(str))
{
return false;
}
DiskLruCache.Editor editor = null;
try
{
editor = cache.edit(generateKey(url));
if(editor != null)
{
OutputStream out = editor.newOutputStream(0);
out.write(str.getBytes());
out.flush();
}
editor.commit();
return true;
} catch (IOException e)
{
e.printStackTrace();
}
return false;
}
/**
* 停止内部正在写缓存的线程,
* 这将导致部分写缓存任务不能进行。
*/
public static void stop()
{
if(service != null)
service.shutdownNow();
}
/**
*
* 根据url获取缓存,并将结果以String形式返回
* @param cache
* @param url
* @return 成功则返回String否则返回null
*/
public static String readCacheToString(DiskLruCache cache,String url)
{
if(cache == null || url == null || TextUtils.isEmpty(url))
return null;
String key = generateKey(url);
DiskLruCache.Snapshot snapshot = null;
try
{
snapshot = cache.get(key);
if(snapshot != null)
{
InputStream in = snapshot.getInputStream(0);
StringBuilder builder = new StringBuilder(1024*2);
int len = 0;
byte[] buffer = new byte[1024];
while((len = in.read(buffer)) != -1)
{
builder.append(new String(buffer,0,len));
}
return builder.toString();
}
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 根据url获取缓存,并将缓存以InputStream形式返回
*
* @param cache DiskLruCache实例
* @param url 缓存名
* @return 命中则返回InputStream流否则返回null
*/
public static InputStream readCacheToInputStream(DiskLruCache cache,String url)
{
if(cache == null || url == null || TextUtils.isEmpty(url))
return null;
String key = generateKey(url);
DiskLruCache.Snapshot snapshot = null;
try
{
snapshot = cache.get(key);
} catch (IOException e)
{
e.printStackTrace();
}
if(snapshot != null)
return snapshot.getInputStream(0);
return null;
}
/**
* 根据url获取缓存,并将缓存以Bitmap形式返回
* @param cache
* @param url
* @return 成功返回bitmap,否则返回null
*/
public static Bitmap readCacheToBitmap(DiskLruCache cache,String url)
{
InputStream in = readCacheToInputStream(cache, url);
if(in != null)
return BitmapFactory.decodeStream(in);
return null;
}
/**
* 获取缓存文件路径(优先选择sd卡)
* @param cacheDirName 缓存文件夹名称
* @param context 上下文
* @return
*/
public static File getDiskCacheDir(String cacheDirName,Context context)
{
String cacheDir;
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)&&!Environment.isExternalStorageRemovable())
{
cacheDir = getExternalCacheDir(context);
if(cacheDir == null)//部分机型返回了null
cacheDir = getInternalCacheDir(context);
}else
{
cacheDir = getInternalCacheDir(context);
}
File dir = new File(cacheDir,cacheDirName);
if(!dir.exists())
dir.mkdirs();
return dir;
}
/**
* 获取当前app版本号
* @param context 上下文
* @return 当前app版本号
*/
public static int getAppVersion(Context context)
{
PackageManager manager = context.getPackageManager();
int code = 1;
try
{
code = manager.getPackageInfo(context.getPackageName(),0).versionCode;
} catch (NameNotFoundException e)
{
e.printStackTrace();
}
return code;
}
/**
* 根据指定的url移除指定缓存
* Note:请不要是使用DiskLruCache.remove()
*
* @param cache
* @param url
* @return
*/
public static boolean remove(DiskLruCache cache,String url)
{
if(cache == null || url == null || TextUtils.isEmpty(url))
{
return false;
}
try
{
return cache.remove(generateKey(url));
} catch (IOException e)
{
e.printStackTrace();
}
return false;
}
/**
* 根据原始键生成新键,以保证键的名称的合法性
* @param key 原始键,通常是url
* @return
*/
public static String generateKey(String key)
{
String cacheKey;
try
{
MessageDigest digest = MessageDigest.getInstance("md5");
digest.update(key.getBytes());
cacheKey = bytesToHexString(digest.digest());
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
private static String bytesToHexString(byte[] bytes)
{
StringBuilder builder = new StringBuilder();
for(int i = 0; i < bytes.length; i++)
{
String hex = Integer.toHexString(0xff&bytes[i]);
if(hex.length() == 1)
builder.append('0');
builder.append(hex);
}
return builder.toString();
}
private static String getExternalCacheDir(Context context)
{
File dir = context.getExternalCacheDir();
if(dir == null)
return null;
if(!dir.exists())
dir.mkdirs();
return dir.getPath();
}
private static String getInternalCacheDir(Context context)
{
File dir = context.getCacheDir();
if(!dir.exists())
dir.mkdirs();
return dir.getPath();
}
}
代码比较简单,而且都有注释,我就不过多解释了。
以后在开发中,如果需要构建DiskLruCache,调用DiskLruCacheHelper.createCache,取缓存调用
DiskLruCacheHelper.
readCacheToXX,写缓存调用
DiskLruCacheHelper.writeXXtoCache,异步写调用
DiskLruCacheHelper.asyncWriteXXtoCache,移除指定缓存调用
DiskLruCacheHelper.remove。是不是很方便?