在Android开发中,如果图片过多,而我们又没有对图片进行有效的缓存,就很容易导致OOM(Out Of Memory)错误。因此,图片的缓存是非常重要的,尤其是对图片非常多的应用。现在很多框架都做了很好的图片缓存处理。
一直想写一个自己的图片缓存框架,之前一直用Glide、Fresco等一些主流的图片框架。这些框架对处理的处理都做的非常好,也查阅了这些框架的一些源码,整体思路是使用三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量。
加载图片时,判断内存缓存中是否有该图片,没有则到本地SD卡中查找,SD卡中没有则通过网络请求获取图片。
因为Android内存空间是很珍贵的,我们不能发过多的图片在缓存中,容易导致OOM,这个时候我们就要使用软引用(SoftReference)。软引用的一个好处是当系统空间紧张的时候,软引用可以随时销毁,因此软引用是不会影响系统运行的,换句话说,如果系统因为某个原因OOM了,那么这个原因肯定不是软引用引起的。
LruCache创建LruCache时,我们需要设置它的大小,一般是系统最大存储空间的八分之一。LruCache的机制是存储最近、最后使用的图片,如果LruCache中的图片大小超过了其默认大小,则会将最老、最远使用的图片移除出去。当图片被LruCache移除的时候,我们需要手动将这张图片添加到软引用(SoftReference)中。我们需要在项目中维护一个由SoftReference组成的集合,其中存储被LruCache移除出来的图片
通过 VImageUtils.disPlay(ImageView ivPic, String url)提供给外部方法进行图片加载。
/**
* 自定义图片缓存工具类
*
* @author Veer
* @email [email protected]
* @date 18/12/11
*/
public class VImageUtils {
private static final String TAG = "VImageUtils";
public static void disPlay(ImageView ivPic, String url) {
Bitmap bitmap;
//内存缓存
bitmap=MemoryCacheUtils.getInstance().getBitmapFromMemory(url);
if (bitmap!=null){
ivPic.setImageBitmap(bitmap);
Log.d(TAG,"从内存获取图片啦.....");
return;
}
//本地缓存
bitmap = LocalCacheUtils.getInstance().getBitmapFromLocal(url);
if(bitmap !=null){
ivPic.setImageBitmap(bitmap);
Log.d(TAG,"从本地获取图片啦.....");
//从本地获取图片后,保存至内存中
MemoryCacheUtils.getInstance().setBitmapToMemory(url,bitmap);
return;
}
//网络缓存
NetCacheUtils.getInstance().getBitmapFromNet(ivPic,url);
}
}
/**
* 网络缓存
*
* @author Veer
* @email [email protected]
* @date 18/12/11
*/
public class NetCacheUtils {
private static NetCacheUtils mInstance;
private static final String TAG = "NetCacheUtils";
private LocalCacheUtils mLocalCacheUtils;
private MemoryCacheUtils mMemoryCacheUtils;
private NetCacheUtils() {
mMemoryCacheUtils = MemoryCacheUtils.getInstance();
mLocalCacheUtils = LocalCacheUtils.getInstance();
}
public static NetCacheUtils getInstance(){
if(mInstance == null){
synchronized (NetCacheUtils.class) {
if (mInstance == null) {
mInstance = new NetCacheUtils();
}
}
}
return mInstance;
}
/**
* 从网络下载图片
* @param ivPic 显示图片的imageview
* @param url 下载图片的网络地址
*/
public void getBitmapFromNet(ImageView ivPic, String url) {
new BitmapTask().execute(ivPic, url);//启动AsyncTask
}
/**
* AsyncTask就是对handler和线程池的封装
* 第一个泛型:参数类型
* 第二个泛型:更新进度的泛型
* 第三个泛型:onPostExecute的返回结果
*/
class BitmapTask extends AsyncTask
/**
* 本地缓存
*
* @author Veer
* @email [email protected]
* @date 18/12/11
*/
public class LocalCacheUtils {
private static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/VImageUtils";
private static LocalCacheUtils mInstance;
private LocalCacheUtils(){
}
public static LocalCacheUtils getInstance(){
if(mInstance == null){
synchronized (LocalCacheUtils.class) {
if (mInstance == null) {
mInstance = new LocalCacheUtils();
}
}
}
return mInstance;
}
/**
* 从本地读取图片
* @param url
* @return Bitmap
*/
public Bitmap getBitmapFromLocal(String url) {
String fileName = null;//把图片的url当做文件名,并进行MD5加密
try {
fileName = MD5Encoder.encode(url);
File file = new File(CACHE_PATH, fileName);
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 从网络获取图片后,保存至本地缓存
* @param url
* @param bitmap
*/
public void setBitmapToLocal(String url, Bitmap bitmap) {
try {
String fileName = MD5Encoder.encode(url);//把图片的url当做文件名,并进行MD5加密
File file = new File(CACHE_PATH, fileName);
//通过得到文件的父文件,判断父文件是否存在
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
//把图片保存至本地
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Map> 软引用中,这样不会造成内存泄露。
/**
* 内存缓存
*
* @author Veer
* @email [email protected]
* @date 18/12/11
*/
public class MemoryCacheUtils {
private static MemoryCacheUtils mInstance;
private ImageCache mImageCache;
private MemoryCacheUtils(){
Map> cacheMap = new HashMap<>();
mImageCache = new ImageCache(cacheMap);
}
public static MemoryCacheUtils getInstance(){
if(mInstance == null){
synchronized (MemoryCacheUtils.class) {
if (mInstance == null) {
mInstance = new MemoryCacheUtils();
}
}
}
return mInstance;
}
/**
* 从内存中读图片
* @param url
*/
public Bitmap getBitmapFromMemory(String url) {
Bitmap bitmap = mImageCache.get(url);
// 如果图片不存在强引用中,则去软引用(SoftReference)中查找
if(bitmap == null){
Map> cacheMap = mImageCache.getCacheMap();
SoftReference softReference = cacheMap.get(url);
if(softReference!=null){
bitmap = softReference.get();
//重新放入强引用缓存中
mImageCache.put(url,bitmap);
}
}
return bitmap;
}
/**
* 往内存中写图片
* @param url
* @param bitmap
*/
public void setBitmapToMemory(String url, Bitmap bitmap) {
mImageCache.put(url,bitmap);
}
}
ImageCache代码:
/**
* 图片缓存
*
* @author Veer
* @email [email protected]
* @date 18/12/11
*/
public class ImageCache extends LruCache {
private Map> cacheMap;
public ImageCache(Map> cacheMap) {
super((int) (Runtime.getRuntime().maxMemory() / 8));
this.cacheMap = cacheMap;
}
@Override // 获取图片大小
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
// 当有图片从LruCache中移除时,将其放进软引用集合中
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue != null) {
SoftReference softReference = new SoftReference(oldValue);
cacheMap.put(key, softReference);
}
}
public Map> getCacheMap() {
return cacheMap;
}
}
以上就是图片缓存的策略,后期我会完善我的这个图片框架,争取做到向那些主流框架一样。
会一直在我的github上更新,希望多多关注