通过DiskLruCache以及LruCache来构建自己项目的图片缓存框架

时间:2023-01-11 21:43:08

前言:

众所周知,对于手机项目而言,图片缓存框架是必不可少的,这样不仅能优化项目的性能,同时也能提高用户体验。git上也有许多开元的图片缓存框架,有很多可以选择,我这仅仅只是教你如何编写一份属于自己的图片缓存框架,这也是我早期之前在其中一个项目中编写的。(仅仅只支持从网络上获取base64转码过来的图片)。 图片缓存基本上,都是通过DiskLruCache以及LruCache来实现的,一个是硬盘缓存,以及一个内存缓存。这两个我就不详细解释了,网上基本上有很多介绍资料。先上一个DiskLruCache下载链接:DiskLruCache 下载 这是我之前用过的DiskLruCache,接下来就是介绍如何构建我们的图片缓存框架了。 基本上的思路是这样的:
1.通过单例模式来构建我们的框架类 2.LruCache和DiskLruCache的内存大小设置 3.优化图片的显示,通过Imageview的宽高来压缩我们的图片 4.处理以上3个点基本上就能构建一个简单的图片缓存框架了,当然具体可以根据自己项目中的具体需求来添加内容,比如增加线程池处理,或者增加图片的处理,或者说根据图片的存储来定时清除内存缓存的图片等等。 代码中基本上都有注释,这里就不详细解释了。项目中的具体处理我就没贴进去了 仅仅只是对于图片缓存的处理。
package cn.com.hnisi.fj.ydzfba.module.ydjcq.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cn.com.hnisi.fj.ydzfba.module.ydjcq.utils.DiskLruCache.Snapshot;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMuxer.OutputFormat;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Base64;
import android.util.Log;


/**
* imagePath是唯一的标识
* */
public class ImageCache {
private FileOutputStream fos;
public DiskLruCache mDiskCache;
public LruCache<String, Bitmap> mMemoryCache;

private ImageCache() {

}

public static ImageCache getInstance() {
return ImageCacheHolder.imageCache;
}

public static class ImageCacheHolder {
public static final ImageCache imageCache = new ImageCache();
}


public void initCache(Context context) {
/** 初始化LruCache 获取内存的1/8*/
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// TODO Auto-generated method stub
return bitmap.getByteCount();
}
};

/** 初始化DiskLruCache */
try {
File cacheDir = getDiskCacheDir(context, "xcdc");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
mDiskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/** 内存缓存的获取 */
public Bitmap getBitmapFromMemoryCache(String imagePath) {
return mMemoryCache.get(imagePath);
}

/** 把图片放到内存缓存中 */
public void addBitmapToMemoryCache(String imagePath, Bitmap bitmap) {
if (mMemoryCache.get(imagePath) == null) {
mMemoryCache.put(imagePath, bitmap);
}
}

/** 本地缓存,通过diskLruCache把图片放到本地磁盘中
* @throws IOException */
public void addBitmapByDiskLruCache(Bitmap bitmap, String imagePath, int reqWidth, int reqHeight){
Snapshot snapshot = null;
BufferedInputStream in = null;
BufferedOutputStream out = null;
String key = imagePath;
// 查找对应缓存的key
try {
snapshot = mDiskCache.get(key);
if (snapshot == null) {
// 如何没有找到对应的缓存,则把图片添加到缓存中(DiskLruCache))
DiskLruCache.Editor editor = mDiskCache.edit(key);
if (editor != null) {
if (bitmap != null) {
// 获取editor的outputStream
OutputStream outputStream = editor.newOutputStream(0);
// bitmap转化为inputStream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 30, baos);
InputStream is = new ByteArrayInputStream(baos.toByteArray());
in = new BufferedInputStream(is, 8 * 1024);
out = new BufferedOutputStream(outputStream, 8 * 1024);
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
editor.commit();

} else {
editor.abort();
}
mDiskCache.flush();
}

}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}

if (out != null) {
out.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

//图片是否在DiskLruCache中
public boolean isInDiskLruCache(String imagePath){
Snapshot snapshot = null;
boolean flag = false;
try {
snapshot = mDiskCache.get(imagePath);
if(snapshot == null){
flag = false;
}else{
flag = true;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return flag;
}

//从DiskLruCache中移除旧缓存
public void removeImageFromDiskLruCache(String imagePath){
try {
mDiskCache.remove(imagePath);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**通过本地缓存获取图片
* @throws IOException */
public Bitmap getBitmapFromDiskLruCache(String imagePath, int reqWidth, int reqHeight){
if(mDiskCache ==null){
return null;
}

Bitmap bitmap = null;
DiskLruCache.Snapshot snapshot;
try {
snapshot = mDiskCache.get(imagePath);
if(snapshot != null){
FileInputStream fis = (FileInputStream) snapshot.getInputStream(0);
FileDescriptor fileDescriptor = fis.getFD();
bitmap = decodeSampleBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight);
if(bitmap!=null){
addBitmapToMemoryCache(imagePath, bitmap);
}
}


} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return bitmap;
}

/**
* File文件中的bitmap的获取,获取后的bitmap是经过压缩的 内存缓存
*/
public Bitmap decodeSampleBitmapFromFile(String filePath, int reqWidth,
int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);

options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, options);
}

/**
* 通过DiskLruCache,的FileInputStream对象来获取Bitmap 本地缓存
*/
public Bitmap decodeSampleBitmapFromFileDescriptor(FileDescriptor fd,
int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fd, null, options);

options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fd, null, options);

}

/**
* 计算bitmap的InSimpleSize 内存缓存
*/
public int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqWidth || width > reqHeight) {
final int halfWidth = height / 2;
final int halfHeight = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}

/** 获取file,得到缓存路径,本地缓存 */
public File getDiskCacheDir(Context context, String fileName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}

return new File(cachePath + File.separator + fileName);
}

/** 清除内存缓存 */
public static void clearSARYCache(String dir) {
File file = new File(dir);
if (!file.exists()) {
file.mkdirs();
}
File[] files = file.listFiles();
for (File mFile : files) {
mFile.delete();
}

}

public static Bitmap base64ToBitmap(String base64String) {
byte[] bytes = Base64.decode(base64String, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
return bitmap;
}
}


当然,你也可以把这个类的功能拆分,把图片的压缩,和缓存的创建以及put/get放入到不同的类中,这样就减少了代码的耦合度,同时也方便以后扩展。由于这是早期的代码,并且也只有base64形式的图片数据,所以也就没对这个框架进行重构了。