图片三级缓存的重要性
很多时候我们都需要从网络上下载图片,如果在图片很多的情况下,每次启动app都要从网上下载,就会造成流量的浪费,影响用户的体验。因此,要利用缓存来避免图片的重复加载。
图片缓存方式
所谓三级缓存,即:
- 网络缓存
- 内存缓存
- 本地缓存
在加载图片时,优先访问内存缓存,如果没有结果,再去访问本地文件;如果都没有结果,才去从网络下载图片。并且在图片下载之后,要将图片缓存到内存和本地。
其实严格来说网络并不能算是缓存,不过这里姑且这么叫吧。
依次实现三级缓存
1.本地缓存
private static final String LOCAL_CACHE_PATH =
Environment.getExternalStorageDirectory().getAbsolutePath() + "/ImgCache";//本地文件保存路径
/**
* 从本地获取图片
*/
private Bitmap getBitmapFromLocalCache(String url) {
Bitmap bitmap = null;
String fileName = url;
File file = new File(LOCAL_CACHE_PATH, fileName);//将url作为文件名
try {
bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bitmap;
}
/**
* 将图片保存到本地
*/
private void addBitmapToLocalCache(String url, Bitmap bitmap) {
String fileName = url;
File file = new File(LOCAL_CACHE_PATH, fileName);//将url作为文件名
File parentFile = file.getParentFile();
if (!parentFile.exists()) {//判断父文件是否存在
parentFile.mkdirs();
}
try {
//保存图片
bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
2.内存缓存
这里使用LruCache()——近期最少使用算法来控制缓存的大小,以避免出现OOM。
private static LruCache<String, Bitmap> memoryCaches;
//私有化构造方法以保证对象全局唯一性
private ImageLoader(ListView listView) {
this.listView = listView;
tasks = new HashSet<>();
//设定缓存区的大小
if (memoryCaches == null) {
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 4;
memoryCaches = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
}
//单例化ImageLoader
public static ImageLoader getImageLoader(ListView mListView) {
imageLoader = new ImageLoader(mListView);
return imageLoader;
}
在设定好内存缓存大小之后,同样需要添加相应的方法。
/**
* 从内存缓存获取bitmap
*/
private Bitmap getBitmapFromLruCache(String url) {
return memoryCaches.get(url);
}
/**
* 向内存缓存中添加bitmap
*/
private void addBitmapToLruCache(String url, Bitmap bitmap) {
if (getBitmapFromLruCache(url) == null) {
memoryCaches.put(url, bitmap);
}
}
3.网络缓存
从网络下载图片,通过AsyncTask来进行异步加载。
/**
* 利用AsyncTask来完成异步加载图片
*/
private class imgAsyncTask extends AsyncTask<String, Void, Bitmap> {
private String url;
public imgAsyncTask(String url) {
this.url = url;
}
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = getBitmapFromUrl(url);
if (bitmap != null) {
addBitmapToLruCache(url, bitmap);//将bitmap添加到缓存中
addBitmapToLocalCache(url, bitmap);//将bitmap添加到本地
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView = (ImageView) listView.findViewWithTag(url);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
tasks.remove(this);
}
}
/**
* 开启网络请求
* 根据url来获取bitmap
*/
private Bitmap getBitmapFromUrl(String urlString) {
Bitmap bitmap = null;
HttpURLConnection connection = null;
try {
URL mUrl = new URL(urlString);
connection = (HttpURLConnection) mUrl.openConnection();
bitmap = BitmapFactory.decodeStream(connection.getInputStream());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
return bitmap;
}
这样三级缓存就写好了,相应的加载方法如下:
//优先从内存获取图片,若不存在则从本地获取
Bitmap bitmap = getBitmapFromLruCache(imgUrl);
if (bitmap == null) {
bitmap = getBitmapFromLocalCache(imgUrl);
}
//若bitmap存在,直接设置
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
//若不存在,开启线程从网络下载
imgAsyncTask mTask = new imgAsyncTask(imgUrl);
mTask.execute(imgUrl);
tasks.add(mTask);
}
最后附上自己项目的地址:https://github.com/Zhai-Wang/KanZhiHu,欢迎各路大神指正!