1.DiskLruCache的创建
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)使用此方法创建DiskLruCache,此方法共接收四个参数
第一个参数表示磁盘缓存在文件系统中的路径,具体为/sdcard/Android/data/package_name/cache目录,其中package_name为应用程序的包名,当应用程序被卸载后此目录会一并删除。
获取磁盘缓存在文件系统中的路径具体代码:
public File getDiskCacheDir(Context context,String uniqueName){
String cachePath;
//判断sd卡是否有读写权限,是否可被移除;当具有读写权限或者不可被移除,使用前者;否则使用后者
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageSt
ate())||!Environment.isExternalStorageRemovable(){
//获取缓存路径;
cachePath = context.getExternalCacheDir().getPath();
}else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath+File.separator+uniqueName);
}
前者获取的路径为:/sdcard/Android/data/package_name/cache后者获取的路劲为: /data/data/package_name/cache,uniqueName为某一类型的数据的值,如图片类型可设置为bitmap,则最终获取的缓存路径为:/sdcard/Android/data/package_name/cache/bitmap
第二个参数为应用的版本号一般设置为1即可,当版本发生改变时会清空之前的缓存文件。
第三个参数为单个节点所对应的数据个数,设置为1即可,第四个参数表示缓存容器的总大小,如50MB,当需要缓存的文件超过该值时会自动清除部分缓存,从而保证缓存文件的大小不超过该缓存容器的大小。
创建一个open()可写为:
File cacheDir = getDiskCacheDir(context,"bitmap");
if(!cacheDir.exists()){
//如果不存在,创建此文件夹
cacheDir.mkdirs();
}
try{
mDiskLruCache = DiskLruCache.open(cacheDir,1,1,50*1024*1024);
} catch (IOException e) {
e.printStackTrace();
}
获取DiskLruCache实例后,接着就可以写入缓存
首先下载一张图片如http://img.my.csdn.NET/uploads/201309/01/1378037235_7476.jpg
private boolean downLoadUrlToStream(String url, final OutputStream outputStream){
OkHttpClient mOkHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
Response response = mOkHttpClient.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
in = new BufferedInputStream(inputStream, 8 * 1024);
out= new BufferedOutputStream(outputStream, 8 * 1024);
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
return true;
}catch (IOException e) {
e.printStackTrace();
}finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
DiskLruCache缓存的添加是通过Editor完成,然后将图片的url转化为key,根据key然后调用edit(String key),可以获取Editor对象,然后调用Editor对象的newOutputStream(int index)方法即可获取OutputStream对象,得到OutputStream对象和url后,使用上述downLoadUrlToStream(String url, final OutputStream outputStream),就可以将图片进行写入,但此时并没有写入文件系统还需调用Editor的commit()方法。
由于url中可能包含特殊字符,使其不能直接作为key,因此可使用MD5编码使其转化为key;
public static String hashKeyForUrl(String url){
String catchKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
catchKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
catchKey = String.valueOf(url.hashCode());
}
return catchKey;
}
private static 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();
}
然后就可以获取Editor对像,并进行写入磁盘操作
public void ImageLoading(){
new Thread(new Runnable() {
@Override
public void run() {
String url = "https://img-my.csdn.net/uploads/201309/01/1378
037235_7476.jpg";
String key = hashKeyForUrl(url);
try {
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor!=null){
OutputStream outputStream = editor.newOutputStream(
0);
if(downLoadUrlToStream(url,outputStream)){
//进行写入操作
editor.commit();
}else {
//下载过程中出现异常退出操作
editor.abort();
}
}
//这个方法用于将内存中的操作记录同步到日志文件(也就是journal文
件)当中。
mDiskLruCache.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
读取缓存
缓存读取也需要将url转化为key,按照MD5上述编码方式转化即可。然后通过DiskLruCache的get(String key)方法获取Snapshot对象,接着通过Snapshot对象的getInputStream(int index)即可获取InputStream,然后就可以获取Bitmap对象。为了避免OOM需要对图片压缩后,再将其显示在ImageView上面。
Bitmap bitmap = null;
String url = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
String key = hashKeyForUrl(url);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
FileInputStream fileInputStream = (FileInputStream) snapshot.getInputSt
ream(0);
//获取文件流对应的文件描述符
FileDescriptor fileDescriptor = fileInputStream.getFD();
//把图片压缩成100*100像素
bitmap = decodeBitmapFromFileDescriptor(fileDescriptor, 100,100);
}
图片压缩
public static Bitmap decodeBitmapFromFileDescriptor(FileDescriptor fileDescriptor,int reqWidth,int reqHeight){
final BitmapFactory.Options options = new BitmapFactory.Options();
//inJustDecodeBounds 设置为true时BitmapFactory只会解析原始图片宽高,不会加载
图片
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeigh
t);
//当希望获取Bitmap实例时需要设置inJustDecodeBounds为false;
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
//获取原始图片的宽高
final int realWidth = options.outWidth;
final int realHeight = options.outHeight;
//定义采样率;
int inSampleSize = 1;
if(realHeight>reqHeight||realWidth>reqWidth){
final int halfHeight = realHeight/2;
final int halfWidth = realWidth/2;
while ((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize
)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
压缩图片时由于是FileInputStream方式,FileInputStream是一种有序的文件流,两次调用decodeStream影响了文件流的位置属性,导致第二次调用时返回null,为了解决此问题可以使用文件流对应的文件描述符,然后再进行加载。