现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在目前市场的应用以及纯图片应用(比如百度美拍)中比较多。
实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存(memory)-本地(local)-网络(Internet) 三层cache机制,其实网络不算cache。
当根据url向网络拉取图片的时候,先从本应用内存中找,如果内存中没有,再从本地缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图 片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个 url被下载过,其图片就被缓存起来了。
三级缓存的比较:
-内存缓存,优先加载,速度最快
-本地缓存,次优先加载,速度快
-网络缓存,不优先加载,速度慢,浪费流量。
现在我们来看看怎么去实现这个缓存。
首先是网络缓存的类:
/**
*
* 从网络下载图片
* @lly
*
*/
public class NetCacheUtils {
private LocalCacheUtils mlocalcacheutils;
private MemoryCacheUtils mmemorycacheutils;
public NetCacheUtils(LocalCacheUtils localcacheutils, MemoryCacheUtils memorycacheutils) {
mlocalcacheutils=localcacheutils;
mmemorycacheutils=memorycacheutils;
}
public void getBitmapFromNet(ImageView iv_photo, String url) {
// TODO Auto-generated method stub
BitmapTask bitmaptask=new BitmapTask();
bitmaptask.execute(iv_photo,url);//开启AsyncTask,参数在doInBackground获取
}
/*AsyncTask 异步任务即做一些简单的异步处理 ;是handle与线程池的封装
* 第一个泛型:参数类型泛型
* 第二个泛型:更新进度泛型
* 第三个泛型:onProgressUpdate的返回结果的泛型
*
*/
class BitmapTask extends AsyncTask<Object, Void, Bitmap>{
private ImageView pic;
private String murl;
/**
* 后台耗时方法在此执行,子线程
*/
@Override
protected Bitmap doInBackground(Object... params) {
pic = (ImageView) params[0];
murl = (String) params[1];
pic.setTag(murl);//将图片与url绑定
return downloadBitmap(murl);
}
/**
* 更新进度,主线程
*/
@Override
protected void onProgressUpdate(Void... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
/**
* 后台耗时方法结束之后,在此执行,主线程
*/
@Override
protected void onPostExecute(Bitmap result) {
if(result!=null){
String tag = (String) pic.getTag();
if(tag.equals(murl)){
pic.setImageBitmap(result);
}
}
mlocalcacheutils.setBitmapTolocal(murl, result);
mmemorycacheutils.setBitmapTomemory(murl, result);
System.out.println("从网络上加载图片啦");
}
}
/**
*
* 下载图片
* @return
*/
private Bitmap downloadBitmap(String url){
HttpURLConnection conn=null;
try {
conn=(HttpURLConnection) new URL(url)
.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
conn.connect();
int responseCode = conn.getResponseCode();//响应码
if(responseCode==200){//表示成功连接
InputStream inputStream = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
conn.disconnect();
}
return null;
}
}
本地缓存的实现:
public class LocalCacheUtils {
private static final String CACHE_PATH=Environment.getExternalStorageDirectory()
.getAbsolutePath()+"/zhbj_cache_52";
/**
*
* 从本地读图片
* @param url
*/
public Bitmap getBitmapFromlocal(String url){
try {
String filename = MD5Encoder.encode(url);
File file=new File(CACHE_PATH, filename);//通过父文件夹与自己的文件名称来创建一个文件
if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
return bitmap;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
*
* 将图片写到本地
* @param url
* @param bitmap
*/
public void setBitmapTolocal(String url,Bitmap bitmap){
try {
String filename = MD5Encoder.encode(url);
File file=new File(filename);
File parentFile = file.getParentFile();
if(!parentFile.exists()){//如果文件夾不存在,則创建
file.mkdirs();
}
//将图片保存到本地
bitmap.compress(CompressFormat.JPEG, 100,
new FileOutputStream(file));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
接下来是内存缓存的实现:
/**
*
* 内存缓存
* @lly
*
*/
public class MemoryCacheUtils {
private HashMap<String, Bitmap> hashlist=new HashMap<String, Bitmap>();
/**
*
* 从内存中读
* @param url
* @return
*/
public Bitmap getBitmapFrommemory(String url){
Bitmap bitmap = hashlist.get(url);
return bitmap;
}
/**
*
* 写入内存
* @param url
* @param bitmap
*/
public void setBitmapTomemory(String url,Bitmap bitmap){
hashlist.put(url, bitmap);
}
}
最后是我们自己实现的图片加载工具:
/**
*
* 图片加载工具
* @lly
*
*/
public class BitMaputils {
NetCacheUtils netcache;
LocalCacheUtils localcacheutils;
MemoryCacheUtils memorycacheutils;
public BitMaputils(){
memorycacheutils=new MemoryCacheUtils();
localcacheutils=new LocalCacheUtils();
netcache=new NetCacheUtils(localcacheutils,memorycacheutils);
}
Bitmap bitmap =null;
public void display(ImageView iv_photo, String url) {
iv_photo.setImageResource(R.drawable.news_pic_default);//默认图片,防止图片的复用
//内存缓存
bitmap= memorycacheutils.getBitmapFrommemory(url);
if(bitmap!=null){
iv_photo.setImageBitmap(bitmap);
System.out.println("从内存中读取图片");
return;
}
//本地缓存
bitmap = localcacheutils.getBitmapFromlocal(url);
if(bitmap!=null){
iv_photo.setImageBitmap(bitmap);
memorycacheutils.setBitmapTomemory(url, bitmap);
System.out.println("从本地读取图片");
return;//从本地读取就不需要从网络读取了
}
//网络缓存(第一次)
netcache.getBitmapFromNet(iv_photo,url);
}
}
图片框架:Android-Universal-Image-Loader 也是采用了三级缓存的思路。