图片三级缓存
什么是三级缓存?
1). 图片的三级缓存
一级缓存: 内存缓存, 缓存的是bitmap对象, 用Map<String, Bitmap>结构保存, key是url
二级缓存: 本地(sd卡)缓存, 缓存的是图片文件, /storage/sdcard/Android/data/packageName/files/图片文件名(xxx.jpg)
三级缓存: 远程服务器缓存, 缓存的是图片文件, 远程服务器上的应用中
2). 如何使用三级缓存? -----如何根据图片的url动态显示图片?
String imagePath = http://p4.so.qhmsg.com/bdr/240/t0157596cfec8884249.jpg和ImageView对象
1). 根据url从一级缓存中取对应的bitmap对象
如果有, 显示(结束)
如果没有, 进入2)
2). 从二级缓存中查找: 得到文件名并在sd卡的缓存目录下加载对应的图片得到Bitmap对象
如果有: 显示, 缓存到一级缓存中(结束)
如果没有, 进入3)
3). 显示代表提示正在加载的图片, 启动分线程联网请求得到Bitmap对象
如果没有: 显示提示错误的图片(结束)
如果有:
显示
缓存到一级缓存
缓存到二级缓存
在ListView使用图片三级缓存会存在图片闪动的bug,如何解决
1). 原因
converView被复用了
2). 解决
a. 每次getView()都将图片的url保存到ImageView上: imageView.setTag(imagePath)
b. 在分线程准备请求服务器加载图片之前, 比较准备加载图片的url与ImageView中保存的最新图片的url是同一个,
如果不是同一个, 当前加载图片的任务不应该再执行
如果相同, 继续执行加载远程图片
c. 在主线程准备显示图片之前, 比较加载到图片的url与ImageView中保存的最新图片的url是同一个 如果不是同一个, 不需要显示此图片 如果相同, 显示图片
代码部分
首先准备20张图片的URL,我这里是随机找的20张美女图片,什么类型的都有,你们也可以自己找,风骚的性感的,可爱的,校花啥的,你开心就好。
public class ImageUrl {
public static final String image1="http://p4.so.qhmsg.com/bdr/_240_/t0157596cfec8884249.jpg";
public static final String image2="http://p3.so.qhimgs1.com/bdr/_240_/t0125644adcf2cb71d3.jpg";
public static final String image3="http://p3.so.qhimgs1.com/bdr/_240_/t01d31b4df909c2aa13.jpg";
public static final String image4="http://p0.so.qhmsg.com/bdr/_240_/t011fc15c16f31bb545.jpg";
public static final String image5="http://p4.so.qhmsg.com/bdr/_240_/t0187439344feb62039.jpg";
public static final String image6="http://p4.so.qhmsg.com/bdr/_240_/t01c867a331ee601f09.jpg";
public static final String image7="http://p1.so.qhimgs1.com/bdr/_240_/t01d207c6bb9e46c6b1.jpg";
public static final String image8="http://p0.so.qhmsg.com/bdr/_240_/t01d5ada99254f1b685.jpg";
public static final String image9="http://p0.so.qhimgs1.com/bdr/_240_/t019aa0ed00b08bc6ec.jpg";
public static final String image10="http://p1.so.qhmsg.com/bdr/_240_/t011a45058123590d96.jpg";
public static final String image11="http://p0.so.qhmsg.com/bdr/_240_/t015a2d44aeee1bcf7f.jpg";
public static final String image12="http://p1.so.qhimgs1.com/bdr/_240_/t01c55edc357b953b51.jpg";
public static final String image13="http://p1.so.qhimgs1.com/bdr/_240_/t017e236ece120c9721.jpg";
public static final String image14="http://p1.so.qhmsg.com/bdr/_240_/t01c9af3809a7b74ec6.jpg";
public static final String image15="http://p3.so.qhmsg.com/bdr/_240_/t0169f347f8991857f8.jpg";
public static final String image16="http://p1.so.qhimgs1.com/bdr/_240_/t016333a4d8144d0681.jpg";
public static final String image17="http://p0.so.qhimgs1.com/bdr/_240_/t0123f85ae6dba80770.jpg";
public static final String image18="http://p2.so.qhimgs1.com/bdr/_240_/t010d8bc3205ced32a2.jpg";
public static final String image19="http://p1.so.qhmsg.com/bdr/_240_/t014976fa8e0ae97556.jpg";
public static final String image20="http://p1.so.qhimgs1.com/bdr/_240_/t01a846efd527e8acc1.jpg";
}
图片三级缓存工具类封装
public class ImageLoader {
// 用于缓存Bitmap对象
private Map<String, Bitmap> cacheMap = new HashMap<String, Bitmap>();
private Context context;
private int loadingImageRes;
private int errorImageRes;
public ImageLoader(Context context, int loadingImageRes, int errorImageRes) {
this.context = context;
this.loadingImageRes = loadingImageRes;
this.errorImageRes = errorImageRes;
}
// 加载图片并且显示
public void loadImage(String imagePath, ImageView imageView) {
//将需要显示的图片url保存到视图上
imageView.setTag(imagePath);
/**
* 1). 根据url从一级缓存中取对应的bitmap对象 如果有, 显示(结束) 如果没有, 进入2)
*/
Bitmap bitmap = getFromFirstCache(imagePath);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
/**
* 2). 从二级缓存中查找: 得到文件名并在sd卡的缓存目录下加载对应的图片得到Bitmap对象 如果有: 显示, 缓存到一级缓存中(结束)
* 如果没有, 进入3)
*
* /storage/sdcard/Android/data/packageName/files/图片文件名(xxx.jpg)
*/
bitmap = getFromSecondCache(imagePath);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
cacheMap.put(imagePath, bitmap);
return;
}
/**
* 3). 显示代表提示正在加载的图片, 启动分线程联网请求得到Bitmap对象 如果没有: 显示提示错误的图片(结束) 如果有:
* 缓存到一级缓存(分线程) 缓存到二级缓存(分线程) 显示(主线程)
*/
loadBitmapFromThirdCache(imagePath, imageView);
}
/**
* 根据图片url从三级缓存中取对应的bitmap对象并显示
*
* @param imagePath
* @param imageView AsyncTask loadBitmapFromThirdCache("../b.jpg", imageView)
* loadBitmapFromThirdCache("../f.jpg",
* imageView)--->imageView.setTag("../f.jpg")
*/
private void loadBitmapFromThirdCache(final String imagePath,
final ImageView imageView) {
new AsyncTask<Void, Void, Bitmap>() {
// 图片出现之前加载loading
@Override
protected void onPreExecute() {
imageView.setImageResource(loadingImageRes);
}
// 请求网络得到图片
@Override
protected Bitmap doInBackground(Void... params) {
Bitmap bitmap = null;
try {
//在准备请求服务器图片之前, 判断是否需要加载
String newImagePath = (String) imageView.getTag();
if (newImagePath != imagePath) {//在准备请求服务器图片之前, 判断是否需要加载
return null;
}
// 得到连接
URL url = new URL(imagePath);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
// 连接
conn.connect();
// 发请求读取返回的数据并封装为bitmap
int code = conn.getResponseCode();
if (code == 200) {
InputStream inputStream = conn.getInputStream();
// 将is封装为bitmap
bitmap = BitmapFactory.decodeStream(inputStream);
// 关闭
inputStream.close();
if (bitmap != null) { // 缓存到一级缓存(分线程)
cacheMap.put(imagePath, bitmap);
// 缓存到二级缓存(分线程) // /storage/sdcard/Android/data/packageName/files/
String filesPath = context.getExternalCacheDir().getAbsolutePath();
// 截取到/xx.jpg
String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
String filePath = filesPath + fileName;
// 缓存到二级缓存中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath));
}
}
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//从联网请求图片到得到图片对象需要一定的时间, 视图可能被复用了,不需要显示
//在主线程准备显示图片之前, 需要判断是否需要显示
String newImagePath = (String) imageView.getTag();
if (newImagePath != imagePath) {//视图已经被复用了
return;
}
// 如果没有: 显示提示错误的图片(结束)
//如果没有: 显示提示错误的图片(结束)
if (bitmap == null) {
imageView.setImageResource(errorImageRes);
} else {//如果有, 显示
imageView.setImageBitmap(bitmap);
}
}
}.
execute();
}
/**
* 根据图片url从二级缓存中取对应的bitmap对象
*
* @param imagePath
* @return
*/
private Bitmap getFromSecondCache(String imagePath) {
// /storage/sdcard/Android/data/packageName/files/
String filesPath = context.getExternalCacheDir().getAbsolutePath();
// String iamgePath =
// https://ps.ssl.qhimg.com/sdmt/89_135_100/t015c74e0a1e3eaf370.jpg
String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
String filePath = filesPath + "/" + fileName;
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
return bitmap;
}
/**
* 根据图片url从一级缓存中取对应的bitmap对象
*
* @param imagePath
* @return
*/
private Bitmap getFromFirstCache(String imagePath) {
// TODO Auto-generated method stub
return cacheMap.get(imagePath);
}
}
适配器的getView中使用:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(parent.getContext(), R.layout.item, null);
holder = new ViewHolder();
holder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ImageLoader imageLoader = new ImageLoader(MainActivity.this, R.drawable.loading, R.drawable.error);
imageLoader.loadImage(images.get(position), holder.imageView);
return convertView;
}
上一篇: 搭建高可用mongodb集群(三)—— 深入副本集内部机制
下一篇: Android图片三级缓存机制