在Android开发中,如果图片过多,而我们又没有对图片进行有效的缓存,就很容易导致OOM(Out Of Memory)错误。因此,图片的缓存是非常重要的,尤其是对图片非常多的应用。现在很多框架都做了很好的图片缓存处理,如Fresco、Glide等。
缓存机制
- 内存缓存,读取速度最快。
- 硬盘缓存(文件缓存),读取速度比内存缓存稍慢。
- 网络缓存,读取速度最慢。
其中内存缓存和硬盘缓存对应的类分别是LruCache和DiskLruCache。Android在Android 3.1加入了LruCache缓存类,其中维护着一个LinkedHashMap。当我们的APP中想要加载某张图片时,先去LruCache中寻找图片,如果LruCache中有,则直接取出来使用,如果LruCache中没有,则去文件系统中寻找,如果有则取出来使用,同时将图片添加到LruCache中,如果没有,则连接网络从网上下载图片。图片下载完成后,将图片通过DiskLruCache缓存到本地,然后放到LruCache中。
实现
public class ImageCache extends LruCache<String, Bitmap> {
private Map<String, SoftReference<Bitmap>> cacheMap;
public ImageCache(Map<String, SoftReference<Bitmap>> cacheMap) {
super((int) (Runtime.getRuntime().maxMemory() / 8));
this.cacheMap = cacheMap;
}
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue != null) {
SoftReference<Bitmap> softReference = new SoftReference<Bitmap>(oldValue);
cacheMap.put(key, softReference);
}
}
public Map<String, SoftReference<Bitmap>> getCacheMap() {
return cacheMap;
}
}
ImageCache继承自LruCache,当图片被LruCache移除的时候,我们需要手动将这张图片添加到软引用(SoftReference)中。我们需要在项目中维护一个由SoftReference组成的集合,其中存储被LruCache移除出来的图片。软引用的一个好处是当系统空间紧张的时候,软引用可以随时销毁,因此软引用是不会影响系统运行的。
工具类
public class CacheUtil {
private static CacheUtil instance;
private Context context;
private ImageCache imageCache;
private CacheUtil(Context context) {
this.context = context;
Map<String, SoftReference<Bitmap>> cacheMap = new HashMap<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { // SDK版本判断
this.imageCache = new ImageCache(cacheMap);
}
}
public static CacheUtil getInstance(Context context) {
if (instance == null) {
synchronized (CacheUtil.class) {
if (instance == null) {
instance = new CacheUtil(context);
}
}
}
return instance;
}
/**
* 将图片添加到缓存中
*/
private void putBitmapIntoCache(String fileName, byte[] data) {
// 将图片的字节数组写入到内存中
FileUtil.getInstance(context).writeFileToStorage(fileName, data);
// 将图片存入强引用(LruCache)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
imageCache.put(fileName, BitmapFactory.decodeByteArray(data, 0, data.length));
}
}
/**
* 从缓存中取出图片
*/
private Bitmap getBitmapFromCache(String fileName) {
// 从强引用(LruCache)中取出图片
Bitmap bm = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { // SDK版本判断
bm = imageCache.get(fileName);
if (bm == null) {
// 如果图片不存在强引用中,则去软引用(SoftReference)中查找
Map<String, SoftReference<Bitmap>> cacheMap = imageCache.getCacheMap();
SoftReference<Bitmap> softReference = cacheMap.get(fileName);
if (softReference != null) {
bm = softReference.get();
imageCache.put(fileName, bm);
} else {
// 如果图片不存在软引用中,则去内存中找
byte[] data = FileUtil.getInstance(context).readBytesFromStorage(fileName);
if (data != null && data.length > 0) {
bm = BitmapFactory.decodeByteArray(data, 0, data.length);
imageCache.put(fileName, bm);
}
}
}
}
return bm;
}
/**
* 使用三级缓存为ImageView设置图片
*/
public void setImageToView(final String path, final ImageView view) {
final String fileName = path.substring(path.lastIndexOf(File.separator) + 1);
Bitmap bm = getBitmapFromCache(fileName);
if (bm != null) {
view.setImageBitmap(bm);
} else {
// 从网络获取图片
new Thread(new Runnable() {
@Override
public void run() {
byte[] b = HttpUtil.getInstance().getByteArrayFromWeb(path);
if (b != null && b.length > 0) {
// 将图片字节数组写入到缓存中
putBitmapIntoCache(fileName, b);
final Bitmap bm = BitmapFactory.decodeByteArray(b, 0, b.length);
// 将从网络获取到的图片设置给ImageView
view.post(new Runnable() {
@Override
public void run() {
view.setImageBitmap(bm);
}
});
}
}
}).start();
}
}
}